diff --git a/chef/cookbooks/apache2/CHANGELOG.md b/chef/cookbooks/apache2/CHANGELOG.md new file mode 100644 index 0000000..f574307 --- /dev/null +++ b/chef/cookbooks/apache2/CHANGELOG.md @@ -0,0 +1,183 @@ +## v1.7.0: + +### Improvement + +- [COOK-3073]: make access.log location configurable per-platform +- [COOK-3074]: don't hardcode the error.log location in the default site config +- [COOK-3268]: don't hardcode DocumentRoot and cgi-bin locations in `default_site` + +### New Feature + +- [COOK-3184]: Add `mod_filter` recipe to Apache2-cookbook +- [COOK-3236]: Add `mod_action` recipe to Apache2-cookbook + +## v1.6.6: + +1.6.4 had a missed step in the automated release, long live 1.6.6. + +### Bug + +- [COOK-3018]: apache2_module does duplicate delayed restart of + apache2 service when conf = true +- [COOK-3027]: Default site enable true, then false, does not disable + default site +- [COOK-3109]: fix apache lib_dir arch attribute regexp + +## v1.6.2 + +* [COOK-2535] - `mod_auth_openid` requires libtool to run autogen.sh +* [COOK-2667] - Typo in usage documentation +* [COOK-2461] - `apache2::mod_auth_openid` fails on some ubuntu systems +* [COOK-2720] - Apache2 minitest helper function `ran_recipe` is not + portable + +## v1.6.0: + +* [COOK-2372] - apache2 mpm_worker: add ServerLimit attribute (default + to 16) + +## v1.5.0: + +**NOTE** The `mod_auth_openid` attributes are changed. The upstream + maintainer deprecated the older release versions, and the source + repository has releases available at specific SHA1SUM references. + The new attribute, `node['apache']['mod_auth_openid']['ref']` is + used to set this. + +* [COOK-2198] - `apache::mod_auth_openid` compiles from source, but + does not install make on debian/ubuntu +* [COOK-2224] - version conflict between cucumber and other gems +* [COOK-2248] - `apache2::mod_php5` uses `not_if` "which php" without + ensuring package 'which' is installed +* [COOK-2269] - Set allow list for mod_status incase external monitor scripts need +* [COOK-2276] - cookbook apache2 documentation regarding listening + ports doesn't match default attributes +* [COOK-2296] - `mod_auth_openid` doesn't have tags/releases for the + version I need for features and fixes +* [COOK-2323] - Add Oracle linux support + +## v1.4.2: + +* [COOK-1721] - fix logrotate recipe + +## v1.4.0: + +* [COOK-1456] - iptables enhancements +* [COOK-1473] - apache2 does not disable default site when setting + "`default_site_enabled`" back to false +* [COOK-1824] - the apache2 cookbook needs to specify which binary is + used on rhel platform +* [COOK-1916] - Download location wrong for apache2 `mod_auth_openid` + >= 0.7 +* [COOK-1917] - Improve `mod_auth_openid` recipe to handle module + upgrade more gracefully +* [COOK-2029] - apache2 restarts on every run on RHEL and friends, + generate-module-list on every run. +* [COOK-2036] - apache2: Cookbook style + +## v1.3.2: + +* [COOK-1804] - fix `web_app` definition parameter so site can be + disabled. + +## v1.3.0: + +* [COOK-1738] - Better configuration for `mod_include` and some + overrides in `web_app` definition +* [COOK-1470] - Change SSL Ciphers to Mitigate BEAST attack + +## v1.2.0: + +* [COOK-692] - delete package conf.d files in module recipes, for EL +* [COOK-1693] - Foodcritic finding for unnecessary string interpolation +* [COOK-1757] - platform_family and better style / usage practices + +## v1.1.16: + +re-releasing as .16 due to error on tag 1.1.14 + +* [COOK-1466] - add `mod_auth_cas` recipe +* [COOK-1609] - apache2 changes ports.conf twice per run when using + apache2::mod_ssl + +## v1.1.12: + +* [COOK-1436] - restore apache2 web_app definition +* [COOK-1356] - allow ExtendedStatus via attribute +* [COOK-1403] - add mod_fastcgi recipe + +## v1.1.10: + +* [COOK-1315] - allow the default site to not be enabled +* [COOK-1328] - cookbook tests (minitest, cucumber) + +## v1.1.8: + +* Some platforms with minimal installations that don't have perl won't + have a `node['languages']['perl']` attribute, so remove the + conditional and rely on the power of idempotence in the package + resource. +* [COOK-1214] - address foodcritic warnings +* [COOK-1180] - add `mod_logio` and fix `mod_proxy` + +## v1.1.6: + +FreeBSD users: This release requires the `freebsd` cookbook. See README.md. + +* [COOK-1025] - freebsd support in mod_php5 recipe + +## v1.1.4: + +* [COOK-1100] - support amazon linux + +## v1.1.2: + +* [COOK-996] - apache2::mod_php5 can cause PHP and module API mismatches +* [COOK-1083] - return string for v_f_p and use correct value for + default + +## v1.1.0: + +* [COOK-861] - Add `mod_perl` and apreq2 +* [COOK-941] - fix `mod_auth_openid` on FreeBSD +* [COOK-1021] - add a commented-out LoadModule directive to keep apxs happy +* [COOK-1022] - consistency for icondir attribute +* [COOK-1023] - fix platform test for attributes +* [COOK-1024] - fix a2enmod script so it runs cleanly on !bash +* [COOK-1026] - fix `error_log` location on FreeBSD + +## v1.0.8: + +* COOK-548 - directory resource doesn't have backup parameter + +## v1.0.6: + +* COOK-915 - update to `mod_auth_openid` version 0.6, see __Recipes/mod_auth_openid__ below. +* COOK-548 - Add support for FreeBSD. + +## v1.0.4: + +* COOK-859 - don't hardcode module paths + +## v1.0.2 + +* Tickets resolved in this release: COOK-788, COOK-782, COOK-780 + +## v1.0.0 + +* Red Hat family support is greatly improved, all recipes except `god_monitor` converge. +* Recipe `mod_auth_openid` now works on RHEL family distros +* Recipe `mod_php5` will now remove config from package on RHEL family so it doesn't conflict with the cookbook's. +* Added `php5.conf.erb` template for `mod_php5` recipe. +* Create the run state directory for `mod_fcgid` to prevent a startup error on RHEL version 6. +* New attribute `node['apache']['lib_dir']` to handle lib vs lib64 on RHEL family distributions. +* New attribute `node['apache']['group']`. +* Scientific Linux support added. +* Use a file resource instead of the generate-module-list executed perl script on RHEL family. +* "default" site can now be disabled. +* web_app now has an "enable" parameter. +* Support for dav_fs apache module. +* Tickets resolved in this release: COOK-754, COOK-753, COOK-665, COOK-624, COOK-579, COOK-519, COOK-518 +* Fix node references in template for a2dissite +* Use proper user and group attributes on files and templates. +* Replace the anemic README.rdoc with this new and improved superpowered README.md :). diff --git a/chef/cookbooks/apache2/CONTRIBUTING.md b/chef/cookbooks/apache2/CONTRIBUTING.md new file mode 100644 index 0000000..3a99897 --- /dev/null +++ b/chef/cookbooks/apache2/CONTRIBUTING.md @@ -0,0 +1,257 @@ +# Contributing to Opscode Cookbooks + +We are glad you want to contribute to Opscode Cookbooks! The first +step is the desire to improve the project. + +You can find the answers to additional frequently asked questions +[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). + +You can find additional information about +[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) +on the wiki as well. + +## Quick-contribute + +* Create an account on our [bug tracker](http://tickets.opscode.com) +* Sign our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) +(keep reading if you're contributing on behalf of your employer) +* Create a ticket for your change on the + [bug tracker](http://tickets.opscode.com) +* Link to your patch as a rebased git branch or pull request from the + ticket +* Resolve the ticket as fixed + +We regularly review contributions and will get back to you if we have +any suggestions or concerns. + +## The Apache License and the CLA/CCLA + +Licensing is very important to open source projects, it helps ensure +the software continues to be available under the terms that the author +desired. Chef uses the Apache 2.0 license to strike a balance between +open contribution and allowing you to use the software however you +would like to. + +The license tells you what rights you have that are provided by the +copyright holder. It is important that the contributor fully +understands what rights they are licensing and agrees to them. +Sometimes the copyright holder isn't the contributor, most often when +the contributor is doing work for a company. + +To make a good faith effort to ensure these criteria are met, Opscode +requires a Contributor License Agreement (CLA) or a Corporate +Contributor License Agreement (CCLA) for all contributions. This is +without exception due to some matters not being related to copyright +and to avoid having to continually check with our lawyers about small +patches. + +It only takes a few minutes to complete a CLA, and you retain the +copyright to your contribution. + +You can complete our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). +If you're contributing on behalf of your employer, have your employer +fill out our +[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) +instead. + +## Ticket Tracker (JIRA) + +The [ticket tracker](http://tickets.opscode.com) is the most important +documentation for the code base. It provides significant historical +information, such as: + +* Which release a bug fix is included in +* Discussion regarding the design and merits of features +* Error output to aid in finding similar bugs + +Each ticket should aim to fix one bug or add one feature. + +## Using git + +You can get a quick copy of the repository for this cookbook by +running `git clone +git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. + +For collaboration purposes, it is best if you create a Github account +and fork the repository to your own account. Once you do this you will +be able to push your changes to your Github repository for others to +see and use. + +If you have another repository in your GitHub account named the same +as the cookbook, we suggest you suffix the repository with -cookbook. + +### Branches and Commits + +You should submit your patch as a git branch named after the ticket, +such as COOK-1337. This is called a _topic branch_ and allows users to +associate a branch of code with the ticket. + +It is a best practice to have your commit message have a _summary +line_ that includes the ticket number, followed by an empty line and +then a brief description of the commit. This also helps other +contributors understand the purpose of changes to the code. + + [COOK-1757] - platform_family and style + + * use platform_family for platform checking + * update notifies syntax to "resource_type[resource_name]" instead of + resources() lookup + * COOK-692 - delete config files dropped off by packages in conf.d + * dropped debian 4 support because all other platforms have the same + values, and it is older than "old stable" debian release + +Remember that not all users use Chef in the same way or on the same +operating systems as you, so it is helpful to be clear about your use +case and change so they can understand it even when it doesn't apply +to them. + +### Github and Pull Requests + +All of Opscode's open source cookbook projects are available on +[Github](http://www.github.com/opscode-cookbooks). + +We don't require you to use Github, and we will even take patch diffs +attached to tickets on the tracker. However Github has a lot of +convenient features, such as being able to see a diff of changes +between a pull request and the main repository quickly without +downloading the branch. + +If you do choose to use a pull request, please provide a link to the +pull request from the ticket __and__ a link to the ticket from the +pull request. Because pull requests only have two states, open and +closed, we can't easily filter pull requests that are waiting for a +reply from the author for various reasons. + +### More information + +Additional help with git is available on the +[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) +wiki page. + +## Functional and Unit Tests + +This cookbook is set up to run tests under +[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It +uses minitest-chef to run integration tests after the node has been +converged to verify that the state of the node. + +Test kitchen should run completely without exception using the default +[baseboxes provided by Opscode](https://github.com/opscode/bento). +Because Test Kitchen creates VirtualBox machines and runs through +every configuration in the Kitchenfile, it may take some time for +these tests to complete. + +If your changes are only for a specific recipe, run only its +configuration with Test Kitchen. If you are adding a new recipe, or +other functionality such as a LWRP or definition, please add +appropriate tests and ensure they run with Test Kitchen. + +If any don't pass, investigate them before submitting your patch. + +Any new feature should have unit tests included with the patch with +good code coverage to help protect it from future changes. Similarly, +patches that fix a bug or regression should have a _regression test_. +Simply put, this is a test that would fail without your patch but +passes with it. The goal is to ensure this bug doesn't regress in the +future. Consider a regular expression that doesn't match a certain +pattern that it should, so you provide a patch and a test to ensure +that the part of the code that uses this regular expression works as +expected. Later another contributor may modify this regular expression +in a way that breaks your use cases. The test you wrote will fail, +signalling to them to research your ticket and use case and accounting +for it. + +If you need help writing tests, please ask on the Chef Developer's +mailing list, or the #chef-hacking IRC channel. + +## Code Review + +Opscode regularly reviews code contributions and provides suggestions +for improvement in the code itself or the implementation. + +We find contributions by searching the ticket tracker for _resolved_ +tickets with a status of _fixed_. If we have feedback we will reopen +the ticket and you should resolve it again when you've made the +changes or have a response to our feedback. When we believe the patch +is ready to be merged, we will tag the _Code Reviewed_ field with +_Reviewed_. + +Depending on the project, these tickets are then merged within a week +or two, depending on the current release cycle. + +## Release Cycle + +The versioning for Opscode Cookbook projects is X.Y.Z. + +* X is a major release, which may not be fully compatible with prior + major releases +* Y is a minor release, which adds both new features and bug fixes +* Z is a patch release, which adds just bug fixes + +A released version of a cookbook will end in an even number, e.g. +"1.2.4" or "0.8.0". When development for the next version of the +cookbook begins, the "Z" patch number is incremented to the next odd +number, however the next release of the cookbook may be a major or +minor incrementing version. + +Releases of Opscode's cookbooks are usually announced on the Chef user +mailing list. Releases of several cookbooks may be batched together +and announced on the [Opscode Blog](http://www.opscode.com/blog). + +## Working with the community + +These resources will help you learn more about Chef and connect to +other members of the Chef community: + +* [chef](http://lists.opscode.com/sympa/info/chef) and + [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing + lists +* #chef and #chef-hacking IRC channels on irc.freenode.net +* [Community Cookbook site](http://community.opscode.com) +* [Chef wiki](http://wiki.opscode.com/display/chef) +* Opscode Chef [product page](http://www.opscode.com/chef) + + +## Cookbook Contribution Do's and Don't's + +Please do include tests for your contribution. If you need help, ask +on the +[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) +or the +[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). +Not all platforms that a cookbook supports may be supported by Test +Kitchen. Please provide evidence of testing your contribution if it +isn't trivial so we don't have to duplicate effort in testing. Chef +10.14+ "doc" formatted output is sufficient. + +Please do indicate new platform (families) or platform versions in the +commit message, and update the relevant ticket. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] - Updated pool resource to correctly + delete.' + +Please do use [foodcritic](http://acrmp.github.com/foodcritic) to +lint-check the cookbook. Except FC007, it should pass all correctness +rules. FC007 is okay as long as the dependent cookbooks are *required* +for the default behavior of the cookbook, such as to support an +uncommon platform, secondary recipe, etc. + +Please do ensure that your changes do not break or modify behavior for +other platforms supported by the cookbook. For example if your changes +are for Debian, make sure that they do not break on CentOS. + +Please do not modify the version number in the metadata.rb, Opscode +will select the appropriate version based on the release cycle +information above. + +Please do not update the CHANGELOG.md for a new version. Not all +changes to a cookbook may be merged and released in the same versions. +Opscode will update the CHANGELOG.md when releasing a new version of +the cookbook. diff --git a/chef/cookbooks/apache2/Gemfile b/chef/cookbooks/apache2/Gemfile new file mode 100644 index 0000000..9dad052 --- /dev/null +++ b/chef/cookbooks/apache2/Gemfile @@ -0,0 +1,10 @@ +source "https://rubygems.org" + +gem 'cucumber', '~> 1.2.0' +gem 'httparty', '~> 0.8.3' +gem 'minitest', '~> 3.0.0' +gem 'nokogiri', '~> 1.5.0' + +group :kitchen do + gem 'test-kitchen', '< 1.0' +end diff --git a/chef/cookbooks/apache2/LICENSE b/chef/cookbooks/apache2/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/apache2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/apache2/README.md b/chef/cookbooks/apache2/README.md new file mode 100644 index 0000000..d594bc1 --- /dev/null +++ b/chef/cookbooks/apache2/README.md @@ -0,0 +1,583 @@ +Description +=========== + +This cookbook provides a complete Debian/Ubuntu style Apache HTTPD +configuration. Non-Debian based distributions such as Red Hat/CentOS, +ArchLinux and others supported by this cookbook will have a +configuration that mimics Debian/Ubuntu style as it is easier to +manage with Chef. + +Debian-style Apache configuration uses scripts to manage modules and +sites (vhosts). The scripts are: + +* a2ensite +* a2dissite +* a2enmod +* a2dismod + +This cookbook ships with templates of these scripts for non +Debian/Ubuntu platforms. The scripts are used in the __Definitions__ +below. + +Requirements +============ + +## Ohai and Chef: + +* Ohai: 0.6.12+ +* Chef: 0.10.10+ + +As of v1.2.0, this cookbook makes use of `node['platform_family']` to +simplify platform selection logic. This attribute was introduced in +Ohai v0.6.12. The recipe methods were introduced in Chef v0.10.10. If +you must run an older version of Chef or Ohai, use [version 1.1.16 of +this cookbook](http://community.opscode.com/cookbooks/apache2/versions/1_1_16/downloads). + +## Cookbooks: + +This cookbook doesn't have direct dependencies on other cookbooks, as +none are needed for the default recipe or the general use cases. + +Depending on your OS configuration and security policy, you may need +additional recipes or cookbooks for this cookbook's recipes to +converge on the node. In particular, the following Operating System +settings may affect the behavior of this cookbook: + +* apt cache outdated +* SELinux enabled +* IPtables +* Compile tools +* 3rd party repositories + +On Ubuntu/Debian, use Opscode's `apt` cookbook to ensure the package +cache is updated so Chef can install packages, or consider putting +apt-get in your bootstrap process or +[knife bootstrap template](http://wiki.opscode.com/display/chef/Knife+Bootstrap). + +On RHEL, SELinux is enabled by default. The `selinux` cookbook +contains a `permissive` recipe that can be used to set SELinux to +"Permissive" state. Otherwise, additional recipes need to be created +by the user to address SELinux permissions. + +The easiest but **certainly not ideal way** to deal with IPtables is +to flush all rules. Opscode does provide an `iptables` cookbook but is +migrating from the approach used there to a more robust solution +utilizing a general "firewall" LWRP that would have an "iptables" +provider. Alternately, you can use ufw, with Opscode's `ufw` and +`firewall` cookbooks to set up rules. See those cookbooks' READMEs for +documentation. + +Build/compile tools may not be installed on the system by default. +Some recipes (e.g., `apache2::mod_auth_openid`) build the module from +source. Use Opscode's `build-essential` cookbook to get essential +build packages installed. + +On ArchLinux, if you are using the `apache2::mod_auth_openid` recipe, +you also need the `pacman` cookbook for the `pacman_aur` LWRP. Put +`recipe[pacman]` on the node's expanded run list (on the node or in a +role). This is not an explicit dependency because it is only required +for this single recipe and platform; the pacman default recipe +performs `pacman -Sy` to keep pacman's package cache updated. + +The `apache2::god_monitor` recipe uses a definition from the `god` +cookbook. Include `recipe[god]` in the node's expanded run list to +ensure that the cookbook is available to the node, and to set up `god`. + +## Platforms: + +The following platforms and versions are tested and supported using +Opscode's [test-kitchen](http://github.com/opscode/test-kitchen). + +* Ubuntu 10.04, 12.04 +* CentOS 5.8, 6.3 + +The following platform families are supported in the code, and are +assumed to work based on the successful testing on Ubuntu and CentOS. + +* Debian +* Red Hat (rhel) +* Fedora +* Amazon Linux + +The following platforms are also supported in the code, have been +tested manually but are not tested under test-kitchen. + +* SUSE/OpenSUSE +* ArchLinux +* FreeBSD + +### Notes for RHEL Family: + +On Red Hat Enterprise Linux and derivatives, the EPEL repository may +be necessary to install packages used in certain recipes. The +`apache2::default` recipe, however, does not require any additional +repositories. Opscode's `yum` cookbook contains a recipe to add the +EPEL repository. See __Examples__ for more information. + +### Notes for FreeBSD: + +The `apache2::mod_php5` recipe depends on the `freebsd` cookbook, +which it uses to set the correct options for compiling the `php5` port +from sources. You need to ensure the `freebsd` is in the expanded run +list, or this recipe will fail. We don't set an explicit dependency +because we feel the `freebsd` cookbook is something users would want +on their nodes, and due to the generality of this cookbook we don't +want additional specific dependencies. + +Tests +===== + +This cookbook in the +[source repository](https://github.com/opscode-cookbooks/apache2) +contains minitest and cucumber tests. This is an initial proof of +concept that will be fleshed out with more supporting infrastructure +at a future time. + +Please see the CONTRIBUTING file for information on how to add tests +for your contributions. + +Attributes +========== + +This cookbook uses many attributes, broken up into a few different +kinds. + +Platform specific +----------------- + +In order to support the broadest number of platforms, several +attributes are determined based on the node's platform. See the +attributes/default.rb file for default values in the case statement at +the top of the file. + +* `node['apache']['dir']` - Location for the Apache configuration +* `node['apache']['log_dir']` - Location for Apache logs +* `node['apache']['error_log']` - Location for the default error log +* `node['apache']['access_log']` - Location for the default access log +* `node['apache']['user']` - User Apache runs as +* `node['apache']['group']` - Group Apache runs as +* `node['apache']['binary']` - Apache httpd server daemon +* `node['apache']['icondir']` - Location for icons +* `node['apache']['cache_dir']` - Location for cached files used by Apache itself or recipes +* `node['apache']['pid_file']` - Location of the PID file for Apache httpd +* `node['apache']['lib_dir']` - Location for shared libraries +* `node['apache']['default_site_enabled']` - Default site enabled. Defaults to true on redhat-family platforms +* `node['apache']['ext_status']` - if true, enables ExtendedStatus for `mod_status` + +General settings +---------------- + +These are general settings used in recipes and templates. Default +values are noted. + +* `node['apache']['listen_ports']` - Ports that httpd should listen on. Default is port 80. +* `node['apache']['contact']` - Value for ServerAdmin directive. Default "ops@example.com". +* `node['apache']['timeout']` - Value for the Timeout directive. Default is 300. +* `node['apache']['keepalive']` - Value for the KeepAlive directive. Default is On. +* `node['apache']['keepaliverequests']` - Value for MaxKeepAliveRequests. Default is 100. +* `node['apache']['keepalivetimeout']` - Value for the KeepAliveTimeout directive. Default is 5. +* `node['apache']['default_modules']` - Array of module names. Can take "mod_FOO" or "FOO" as names, where FOO is the apache module, e.g. "`mod_status`" or "`status`". + +The modules listed in `default_modules` will be included as recipes in `recipe[apache::default]`. + +Prefork attributes +------------------ + +Prefork attributes are used for tuning the Apache HTTPD prefork MPM +configuration. + +* `node['apache']['prefork']['startservers']` - initial number of server processes to start. Default is 16. +* `node['apache']['prefork']['minspareservers']` - minimum number of spare server processes. Default 16. +* `node['apache']['prefork']['maxspareservers']` - maximum number of spare server processes. Default 32. +* `node['apache']['prefork']['serverlimit']` - upper limit on configurable server processes. Default 400. +* `node['apache']['prefork']['maxclients']` - Maximum number of simultaneous connections. +* `node['apache']['prefork']['maxrequestsperchild']` - Maximum number of request a child process will handle. Default 10000. + +Worker attributes +----------------- + +Worker attributes are used for tuning the Apache HTTPD worker MPM +configuration. + +* `node['apache']['worker']['startservers']` - Initial number of server processes to start. Default 4 +* `node['apache']['worker']['serverlimit']` - upper limit on configurable server processes. Default 16. +* `node['apache']['worker']['maxclients']` - Maximum number of simultaneous connections. Default 1024. +* `node['apache']['worker']['minsparethreads']` - Minimum number of spare worker threads. Default 64 +* `node['apache']['worker']['maxsparethreads']` - Maximum number of spare worker threads. Default 192. +* `node['apache']['worker']['maxrequestsperchild']` - Maximum number of requests a child process will handle. + +mod\_auth\_openid attributes +---------------------------- + +The following attributes are in the `attributes/mod_auth_openid.rb` +file. Like all Chef attributes files, they are loaded as well, but +they're logistically unrelated to the others, being specific to the +`mod_auth_openid` recipe. + +* `node['apache']['mod_auth_openid']['checksum']` - sha256sum of the tarball containing the source. +* `node['apache']['mod_auth_openid']['ref']` - Any sha, tag, or branch found from https://github.com/bmuller/mod_auth_openid +* `node['apache']['mod_auth_openid']['cache_dir']` - the cache directory is where the sqlite3 database is stored. It is separate so it can be managed as a directory resource. +* `node['apache']['mod_auth_openid']['dblocation']` - filename of the sqlite3 database used for directive `AuthOpenIDDBLocation`, stored in the `cache_dir` by default. +* `node['apache']['mod_auth_openid']['configure_flags']` - optional array of configure flags passed to the `./configure` step in the compilation of the module. + +mod\_ssl attributes +------------------- + +* `node['apache']['mod_ssl']['cipher_suite']` - sets the + SSLCiphersuite value to the specified string. The default is + considered "sane" but you may need to change it for your local + security policy, e.g. if you have PCI-DSS requirements. Additional + commentary on the + [original pull request](https://github.com/opscode-cookbooks/apache2/pull/15#commitcomment-1605406). + +Recipes +======= + +Most of the recipes in the cookbook are for enabling Apache modules. +Where additional configuration or behavior is used, it is documented +below in more detail. + +The following recipes merely enable the specified module: `mod_alias`, +`mod_basic`, `mod_digest`, `mod_authn_file`, `mod_authnz_ldap`, +`mod_authz_default`, `mod_authz_groupfile`, `mod_authz_host`, +`mod_authz_user`, `mod_autoindex`, `mod_cgi`, `mod_dav_fs`, +`mod_dav_svn`, `mod_deflate`, `mod_dir`, `mod_env`, `mod_expires`, +`mod_headers`, `mod_ldap`, `mod_log_config`, `mod_mime`, +`mod_negotiation`, `mod_proxy`, `mod_proxy_ajp`, `mod_proxy_balancer`, +`mod_proxy_connect`, `mod_proxy_http`, `mod_python`, `mod_rewrite`, +`mod_setenvif`, `mod_status`, `mod_wsgi`, `mod_xsendfile`. + +On RHEL Family distributions, certain modules ship with a config file +with the package. The recipes here may delete those configuration +files to ensure they don't conflict with the settings from the +cookbook, which will use per-module configuration in +`/etc/httpd/mods-enabled`. + +default +------- + +The default recipe does a number of things to set up Apache HTTPd. It +also includes a number of modules based on the attribute +`node['apache']['default_modules']` as recipes. + +logrotate +--------- + +Logrotate adds a logrotate entry for your apache2 logs. This recipe +requires the `logrotate` cookbook; ensure that `recipe[logrotate]` is +in the node's expanded run list. + +mod\_auth\_cas +-------------- + +This recipe installs the proper package and enables the `auth_cas` +module. It can install from source or package. Package is the default, +set the attribute `node['apache']['mod_auth_cas']['from_source']` to +true to enable source installation. Modify the version to install by +changing the attribute +`node['apache']['mod_auth_cas']['source_revision']`. It is a version +tag by default, but could be master, or another tag, or branch. + +The module configuration is written out with the `CASCookiePath` set, +otherwise an error loading the module may cause Apache to not start. + +**Note**: This recipe does not work on EL 6 platforms unless +epel-testing repository is enabled (outside the scope of this +cookbook), or the package version 1.0.8.1-3.el6 or higher is otherwise +available to the system due to this bug: + +https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=708550 + +mod\_auth\_openid +----------------- + +**Changed via COOK-915** + +This recipe compiles the module from source. In addition to +`build-essential`, some other packages are included for installation +like the GNU C++ compiler and development headers. + +To use the module in your own cookbooks to authenticate systems using +OpenIDs, specify an array of OpenIDs that are allowed to authenticate +with the attribute `node['apache']['allowed_openids']`. Use the +following in a vhost to protect with OpenID authentication: + + AuthType OpenID require user <%= node['apache']['allowed_openids'].join(' ') %> + AuthOpenIDDBLocation <%= node['apache']['mod_auth_openid']['dblocation'] %> + +Change the DBLocation with the attribute as required; this file is in +a different location than previous versions, see below. It should be a +sane default for most platforms, though, see +`attributes/mod_auth_openid.rb`. + +### Changes from COOK-915: + +* `AuthType OpenID` instead of `AuthOpenIDEnabled On`. +* `require user` instead of `AuthOpenIDUserProgram`. +* A bug(?) in `mod_auth_openid` causes it to segfault when attempting + to update the database file if the containing directory is not + writable by the HTTPD process owner (e.g., www-data), even if the + file is writable. In order to not interfere with other settings from + the default recipe in this cookbook, the db file is moved. + +mod\_fastcgi +------------ + +Install the fastcgi package and enable the module. + +Only work on Debian/Ubuntu + +mod\_fcgid +---------- + +Installs the fcgi package and enables the module. Requires EPEL on +RHEL family. + +On RHEL family, this recipe will delete the fcgid.conf and on version +6+, create the /var/run/httpd/mod_fcgid` directory, which prevents the +emergency error: + + [emerg] (2)No such file or directory: mod_fcgid: Can't create shared memory for size XX bytes + +mod\_php5 +-------- + +Simply installs the appropriate package on Debian, Ubuntu and +ArchLinux. + +On Red Hat family distributions including Fedora, the php.conf that +comes with the package is removed. On RHEL platforms less than v6, the +`php53` package is used. + +mod\_ssl +-------- + +Besides installing and enabling `mod_ssl`, this recipe will append +port 443 to the `node['apache']['listen_ports']` attribute array and +update the ports.conf. + +god\_monitor +------------ + +Sets up a `god` monitor for Apache. External requirements are the +`god` and `runit` cookbooks from Opscode. When using this recipe, +include `recipe[god]` in the node's expanded run list to ensure the +client downloads it; `god` depends on runit so that will also be +downloaded. + +**Note** This recipe is not tested under test-kitchen yet and is + pending fix in COOK-744. + +Definitions +=========== + +The cookbook provides a few definitions. At some point in the future +these definitions may be refactored into lightweight resources and +providers as suggested by +[foodcritic rule FC015](http://acrmp.github.com/foodcritic/#FC015). + +apache\_conf +------------ + +Sets up configuration file for an Apache module from a template. The +template should be in the same cookbook where the definition is used. +This is used by the `apache_module` definition and is not often used +directly. + +This will use a template resource to write the module's configuration +file in the `mods-available` under the Apache configuration directory +(`node['apache']['dir']`). This is a platform-dependent location. See +__apache\_module__. + +### Parameters: + +* `name` - Name of the template. When used from the `apache_module`, + it will use the same name as the module. + +### Examples: + +Create `#{node['apache']['dir']}/mods-available/alias.conf`. + + apache_conf "alias" + +apache\_module +-------------- + +Enable or disable an Apache module in +`#{node['apache']['dir']}/mods-available` by calling `a2enmod` or +`a2dismod` to manage the symbolic link in +`#{node['apache']['dir']}/mods-enabled`. If the module has a +configuration file, a template should be created in the cookbook where +the definition is used. See __Examples__. + +### Parameters: + +* `name` - Name of the module enabled or disabled with the `a2enmod` or `a2dismod` scripts. +* `enable` - Default true, which uses `a2enmod` to enable the module. If false, the module will be disabled with `a2dismod`. +* `conf` - Default false. Set to true if the module has a config file, which will use `apache_conf` for the file. +* `filename` - specify the full name of the file, e.g. + +### Examples: + +Enable the ssl module, which also has a configuration template in `templates/default/ssl.conf.erb`. + + apache_module "ssl" do + conf true + end + +Enable the php5 module, which has a different filename than the module default: + + apache_module "php5" do + filename "libphp5.so" + end + +Disable a module: + + apache_module "disabled_module" do + enable false + end + +See the recipes directory for many more examples of `apache_module`. + +apache\_site +------------ + +Enable or disable a VirtualHost in +`#{node['apache']['dir']}/sites-available` by calling a2ensite or +a2dissite to manage the symbolic link in +`#{node['apache']['dir']}/sites-enabled`. + +The template for the site must be managed as a separate resource. To +combine the template with enabling a site, see `web_app`. + +### Parameters: + +* `name` - Name of the site. +* `enable` - Default true, which uses `a2ensite` to enable the site. If false, the site will be disabled with `a2dissite`. + +web\_app +-------- + +Manage a template resource for a VirtualHost site, and enable it with +`apache_site`. This is commonly done for managing web applications +such as Ruby on Rails, PHP or Django, and the default behavior +reflects that. However it is flexible. + +This definition includes some recipes to make sure the system is +configured to have Apache and some sane default modules: + +* `apache2` +* `apache2::mod_rewrite` +* `apache2::mod_deflate` +* `apache2::mod_headers` + +It will then configure the template (see __Parameters__ and +__Examples__ below), and enable or disable the site per the `enable` +parameter. + +### Parameters: + +Current parameters used by the definition: + +* `name` - The name of the site. The template will be written to + `#{node['apache']['dir']}/sites-available/#{params['name']}.conf` +* `cookbook` - Optional. Cookbook where the source template is. If + this is not defined, Chef will use the named template in the + cookbook where the definition is used. +* `template` - Default `web_app.conf.erb`, source template file. +* `enable` - Default true. Passed to the `apache_site` definition. + +Additional parameters can be defined when the definition is called in +a recipe, see __Examples__. + +### Examples: + +All parameters are passed into the template. You can use whatever you +like. The apache2 cookbook comes with a `web_app.conf.erb` template as +an example. The following parameters are used in the template: + +* `server_name` - ServerName directive. +* `server_aliases` - ServerAlias directive. Must be an array of aliases. +* `docroot` - DocumentRoot directive. +* `application_name` - Used in RewriteLog directive. Will be set to the `name` parameter. +* `directory_index` - Allow overriding the default DirectoryIndex setting, optional +* `directory_options` - Override Options on the docroot, for example to add parameters like Includes or Indexes, optional. +* `allow_override` - Modify the AllowOverride directive on the docroot to support apps that need .htaccess to modify configuration or require authentication. + +To use the default web_app, for example: + + web_app "my_site" do + server_name node['hostname'] + server_aliases [node['fqdn'], "my-site.example.com"] + docroot "/srv/www/my_site" + end + +The parameters specified will be used as: + +* `@params[:server_name]` +* `@params[:server_aliases]` +* `@params[:docroot]` + +In the template. When you write your own, the `@` is significant. + +For more information about Definitions and parameters, see the +[Chef Wiki](http://wiki.opscode.com/display/chef/Definitions) + +Usage +===== + +Using this cookbook is relatively straightforward. Add the desired +recipes to the run list of a node, or create a role. Depending on your +environment, you may have multiple roles that use different recipes +from this cookbook. Adjust any attributes as desired. For example, to +create a basic role for web servers that provide both HTTP and HTTPS: + + % cat roles/webserver.rb + name "webserver" + description "Systems that serve HTTP and HTTPS" + run_list( + "recipe[apache2]", + "recipe[apache2::mod_ssl]" + ) + default_attributes( + "apache" => { + "listen_ports" => ["80", "443"] + } + ) + +For examples of using the definitions in your own recipes, see their +respective sections above. + +License and Authors +=================== + +* Author:: Adam Jacob +* Author:: Joshua Timberman +* Author:: Bryan McLellan +* Author:: Dave Esposito +* Author:: David Abdemoulaie +* Author:: Edmund Haselwanter +* Author:: Eric Rochester +* Author:: Jim Browne +* Author:: Matthew Kent +* Author:: Nathen Harvey +* Author:: Ringo De Smet +* Author:: Sean OMeara +* Author:: Seth Chisamore +* Author:: Gilles Devaux + +* Copyright:: 2009-2012, Opscode, Inc +* Copyright:: 2011, Atriso +* Copyright:: 2011, CustomInk, LLC. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/apache2/attributes/default.rb b/chef/cookbooks/apache2/attributes/default.rb new file mode 100644 index 0000000..2fed820 --- /dev/null +++ b/chef/cookbooks/apache2/attributes/default.rb @@ -0,0 +1,168 @@ +# +# Cookbook Name:: apache2 +# Attributes:: apache +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['apache']['root_group'] = "root" + +# Where the various parts of apache are +case platform +when "redhat", "centos", "scientific", "fedora", "suse", "amazon", "oracle" + default['apache']['package'] = "httpd" + default['apache']['dir'] = "/etc/httpd" + default['apache']['log_dir'] = "/var/log/httpd" + default['apache']['error_log'] = "error.log" + default['apache']['access_log'] = "access.log" + default['apache']['user'] = "apache" + default['apache']['group'] = "apache" + default['apache']['binary'] = "/usr/sbin/httpd" + default['apache']['docroot_dir'] = "/var/www/html" + default['apache']['cgibin_dir'] = "/var/www/cgi-bin" + default['apache']['icondir'] = "/var/www/icons" + default['apache']['cache_dir'] = "/var/cache/httpd" + if node['platform_version'].to_f >= 6 then + default['apache']['pid_file'] = "/var/run/httpd/httpd.pid" + else + default['apache']['pid_file'] = "/var/run/httpd.pid" + end + default['apache']['lib_dir'] = node['kernel']['machine'] =~ /^i[36]86$/ ? "/usr/lib/httpd" : "/usr/lib64/httpd" + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" + default['apache']['default_site_enabled'] = false +when "debian", "ubuntu" + default['apache']['package'] = "apache2" + default['apache']['dir'] = "/etc/apache2" + default['apache']['log_dir'] = "/var/log/apache2" + default['apache']['error_log'] = "error.log" + default['apache']['access_log'] = "access.log" + default['apache']['user'] = "www-data" + default['apache']['group'] = "www-data" + default['apache']['binary'] = "/usr/sbin/apache2" + default['apache']['docroot_dir'] = "/var/www" + default['apache']['cgibin_dir'] = "/usr/lib/cgi-bin" + default['apache']['icondir'] = "/usr/share/apache2/icons" + default['apache']['cache_dir'] = "/var/cache/apache2" + default['apache']['pid_file'] = "/var/run/apache2.pid" + default['apache']['lib_dir'] = "/usr/lib/apache2" + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" + default['apache']['default_site_enabled'] = false +when "arch" + default['apache']['package'] = "apache" + default['apache']['dir'] = "/etc/httpd" + default['apache']['log_dir'] = "/var/log/httpd" + default['apache']['error_log'] = "error.log" + default['apache']['access_log'] = "access.log" + default['apache']['user'] = "http" + default['apache']['group'] = "http" + default['apache']['binary'] = "/usr/sbin/httpd" + default['apache']['docroot_dir'] = "/srv/http" + default['apache']['cgibin_dir'] = "/usr/share/httpd/cgi-bin" + default['apache']['icondir'] = "/usr/share/httpd/icons" + default['apache']['cache_dir'] = "/var/cache/httpd" + default['apache']['pid_file'] = "/var/run/httpd/httpd.pid" + default['apache']['lib_dir'] = "/usr/lib/httpd" + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" + default['apache']['default_site_enabled'] = false +when "freebsd" + default['apache']['package'] = "apache22" + default['apache']['dir'] = "/usr/local/etc/apache22" + default['apache']['log_dir'] = "/var/log" + default['apache']['error_log'] = "httpd-error.log" + default['apache']['access_log'] = "httpd-access.log" + default['apache']['root_group'] = "wheel" + default['apache']['user'] = "www" + default['apache']['group'] = "www" + default['apache']['binary'] = "/usr/local/sbin/httpd" + default['apache']['docroot_dir'] = "/usr/local/www/apache22/data" + default['apache']['cgibin_dir'] = "/usr/local/www/apache22/cgi-bin" + default['apache']['icondir'] = "/usr/local/www/apache22/icons" + default['apache']['cache_dir'] = "/var/run/apache22" + default['apache']['pid_file'] = "/var/run/httpd.pid" + default['apache']['lib_dir'] = "/usr/local/libexec/apache22" + default['apache']['libexecdir'] = node['apache']['lib_dir'] + default['apache']['default_site_enabled'] = false +else + default['apache']['dir'] = "/etc/apache2" + default['apache']['log_dir'] = "/var/log/apache2" + default['apache']['error_log'] = "error.log" + default['apache']['access_log'] = "access.log" + default['apache']['user'] = "www-data" + default['apache']['group'] = "www-data" + default['apache']['binary'] = "/usr/sbin/apache2" + default['apache']['docroot_dir'] = "/var/www" + default['apache']['cgibin_dir'] = "/usr/lib/cgi-bin" + default['apache']['icondir'] = "/usr/share/apache2/icons" + default['apache']['cache_dir'] = "/var/cache/apache2" + default['apache']['pid_file'] = "logs/httpd.pid" + default['apache']['lib_dir'] = "/usr/lib/apache2" + default['apache']['libexecdir'] = "#{node['apache']['lib_dir']}/modules" + default['apache']['default_site_enabled'] = false +end + +### +# These settings need the unless, since we want them to be tunable, +# and we don't want to override the tunings. +### + +# General settings +default['apache']['listen_ports'] = ["80"] +default['apache']['contact'] = "ops@example.com" +default['apache']['timeout'] = 300 +default['apache']['keepalive'] = "On" +default['apache']['keepaliverequests'] = 100 +default['apache']['keepalivetimeout'] = 5 + +# Security +default['apache']['servertokens'] = "Prod" +default['apache']['serversignature'] = "On" +default['apache']['traceenable'] = "On" + +# mod_auth_openids +default['apache']['allowed_openids'] = Array.new + +# mod_status Allow list, space seprated list of allowed entries. +default['apache']['status_allow_list'] = "localhost ip6-localhost" + +# mod_status ExtendedStatus, set to 'true' to enable +default['apache']['ext_status'] = false + +# Prefork Attributes +default['apache']['prefork']['startservers'] = 16 +default['apache']['prefork']['minspareservers'] = 16 +default['apache']['prefork']['maxspareservers'] = 32 +default['apache']['prefork']['serverlimit'] = 400 +default['apache']['prefork']['maxclients'] = 400 +default['apache']['prefork']['maxrequestsperchild'] = 10000 + +# Worker Attributes +default['apache']['worker']['startservers'] = 4 +default['apache']['worker']['serverlimit'] = 16 +default['apache']['worker']['maxclients'] = 1024 +default['apache']['worker']['minsparethreads'] = 64 +default['apache']['worker']['maxsparethreads'] = 192 +default['apache']['worker']['threadsperchild'] = 64 +default['apache']['worker']['maxrequestsperchild'] = 0 + +# Default modules to enable via include_recipe + +default['apache']['default_modules'] = %w{ + status alias auth_basic authn_file authz_default authz_groupfile authz_host authz_user autoindex + dir env mime negotiation setenvif +} + +%w{ log_config logio }.each do |log_mod| + default['apache']['default_modules'] << log_mod if ["rhel", "fedora", "suse", "arch", "freebsd"].include?(node['platform_family']) +end diff --git a/chef/cookbooks/apache2/attributes/mod_auth_cas.rb b/chef/cookbooks/apache2/attributes/mod_auth_cas.rb new file mode 100644 index 0000000..a5cc262 --- /dev/null +++ b/chef/cookbooks/apache2/attributes/mod_auth_cas.rb @@ -0,0 +1,2 @@ +default['apache']['mod_auth_cas']['from_source'] = false +default['apache']['mod_auth_cas']['source_revision'] = "v1.0.8.1" diff --git a/chef/cookbooks/apache2/attributes/mod_auth_openid.rb b/chef/cookbooks/apache2/attributes/mod_auth_openid.rb new file mode 100644 index 0000000..d6b8f1e --- /dev/null +++ b/chef/cookbooks/apache2/attributes/mod_auth_openid.rb @@ -0,0 +1,32 @@ +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2011, Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['apache']['mod_auth_openid']['ref'] = "95043901eab868400937642d9bc55d17e9dd069f" +default['apache']['mod_auth_openid']['source_url'] = "https://github.com/bmuller/mod_auth_openid/archive/#{node['apache']['mod_auth_openid']['ref']}.tar.gz" +default['apache']['mod_auth_openid']['cache_dir'] = "/var/cache/mod_auth_openid" +default['apache']['mod_auth_openid']['dblocation'] = "#{node['apache']['mod_auth_openid']['cache_dir']}/mod_auth_openid.db" + +case node['platform_family'] +when "freebsd" + default['apache']['mod_auth_openid']['configure_flags'] = [ + "CPPFLAGS=-I/usr/local/include", + "LDFLAGS=-I/usr/local/lib -lsqlite3" + ] +else + default['apache']['mod_auth_openid']['configure_flags'] = [] +end diff --git a/chef/cookbooks/apache2/attributes/mod_ssl.rb b/chef/cookbooks/apache2/attributes/mod_ssl.rb new file mode 100644 index 0000000..c744cb4 --- /dev/null +++ b/chef/cookbooks/apache2/attributes/mod_ssl.rb @@ -0,0 +1,19 @@ +# +# Author:: Nathan L Smith +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['apache']['mod_ssl']['cipher_suite'] = 'RC4-SHA:HIGH:!ADH' diff --git a/chef/cookbooks/apache2/definitions/apache_conf.rb b/chef/cookbooks/apache2/definitions/apache_conf.rb new file mode 100644 index 0000000..5a62158 --- /dev/null +++ b/chef/cookbooks/apache2/definitions/apache_conf.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_conf +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_conf do + template "#{node['apache']['dir']}/mods-available/#{params[:name]}.conf" do + source "mods/#{params[:name]}.conf.erb" + notifies :restart, "service[apache2]" + mode 0644 + end +end diff --git a/chef/cookbooks/apache2/definitions/apache_module.rb b/chef/cookbooks/apache2/definitions/apache_module.rb new file mode 100644 index 0000000..99ca29c --- /dev/null +++ b/chef/cookbooks/apache2/definitions/apache_module.rb @@ -0,0 +1,53 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_module +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_module, :enable => true, :conf => false do + include_recipe "apache2" + + params[:filename] = params[:filename] || "mod_#{params[:name]}.so" + params[:module_path] = params[:module_path] || "#{node['apache']['libexecdir']}/#{params[:filename]}" + + if params[:conf] + apache_conf params[:name] + end + + if platform_family?("rhel", "fedora", "arch", "suse", "freebsd") + file "#{node['apache']['dir']}/mods-available/#{params[:name]}.load" do + content "LoadModule #{params[:name]}_module #{params[:module_path]}\n" + mode 0644 + end + end + + if params[:enable] + execute "a2enmod #{params[:name]}" do + command "/usr/sbin/a2enmod #{params[:name]}" + notifies :restart, "service[apache2]" + not_if do (::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") and + ((::File.exists?("#{node['apache']['dir']}/mods-available/#{params[:name]}.conf"))? + (::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.conf")):(true))) + end + end + else + execute "a2dismod #{params[:name]}" do + command "/usr/sbin/a2dismod #{params[:name]}" + notifies :restart, "service[apache2]" + only_if do ::File.symlink?("#{node['apache']['dir']}/mods-enabled/#{params[:name]}.load") end + end + end +end diff --git a/chef/cookbooks/apache2/definitions/apache_site.rb b/chef/cookbooks/apache2/definitions/apache_site.rb new file mode 100644 index 0000000..1bc2870 --- /dev/null +++ b/chef/cookbooks/apache2/definitions/apache_site.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_site +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_site, :enable => true do + include_recipe "apache2" + + if params[:enable] + execute "a2ensite #{params[:name]}" do + command "/usr/sbin/a2ensite #{params[:name]}" + notifies :restart, resources(:service => "apache2") + not_if do + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") or + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/000-#{params[:name]}") + end + only_if do ::File.exists?("#{node['apache']['dir']}/sites-available/#{params[:name]}") end + end + else + execute "a2dissite #{params[:name]}" do + command "/usr/sbin/a2dissite #{params[:name]}" + notifies :restart, resources(:service => "apache2") + only_if do + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/#{params[:name]}") or + ::File.symlink?("#{node['apache']['dir']}/sites-enabled/000-#{params[:name]}") + end + end + end +end diff --git a/chef/cookbooks/apache2/definitions/web_app.rb b/chef/cookbooks/apache2/definitions/web_app.rb new file mode 100644 index 0000000..2547e30 --- /dev/null +++ b/chef/cookbooks/apache2/definitions/web_app.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: apache2 +# Definition:: web_app +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :web_app, :template => "web_app.conf.erb", :enable => true do + + application_name = params[:name] + + include_recipe "apache2" + include_recipe "apache2::mod_rewrite" + include_recipe "apache2::mod_deflate" + include_recipe "apache2::mod_headers" + + template "#{node['apache']['dir']}/sites-available/#{application_name}.conf" do + source params[:template] + owner "root" + group node['apache']['root_group'] + mode 0644 + if params[:cookbook] + cookbook params[:cookbook] + end + variables( + :application_name => application_name, + :params => params + ) + if ::File.exists?("#{node['apache']['dir']}/sites-enabled/#{application_name}.conf") + notifies :reload, resources(:service => "apache2"), :delayed + end + end + + site_enabled = params[:enable] + apache_site "#{params[:name]}.conf" do + enable site_enabled + end +end diff --git a/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl b/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl new file mode 100644 index 0000000..83f849e --- /dev/null +++ b/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +=begin + +Generates Ubuntu style module.load files. + +./apache2_module_conf_generate.pl /usr/lib64/httpd/modules /etc/httpd/mods-available + +ARGV[0] is the apache modules directory, ARGV[1] is where you want 'em. + +=cut + +use File::Find; + +use strict; +use warnings; + +die "Must have '/path/to/modules' and '/path/to/modules.load'" + unless $ARGV[0] && $ARGV[1]; + +find( + { + wanted => sub { + return 1 if $File::Find::name !~ /\.so$/; + my $modfile = $_; + $modfile =~ /(lib|mod_)(.+)\.so$/; + my $modname = $2; + my $filename = "$ARGV[1]/$modname.load"; + unless ( -f $filename ) { + open( FILE, ">", $filename ) or die "Cannot open $filename"; + print FILE "LoadModule " . $modname . "_module $File::Find::name\n"; + close(FILE); + } + }, + follow => 1, + }, + $ARGV[0] +); + +exit 0; + diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..0093d23 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/default_test.rb @@ -0,0 +1,77 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::default' do + include Helpers::Apache + + it 'installs apache' do + package(node['apache']['package']).must_be_installed + end + + it 'starts apache' do + apache_service.must_be_running + end + + it 'enables apache' do + apache_service.must_be_enabled + end + + it 'creates the conf.d directory' do + directory("#{node['apache']['dir']}/conf.d").must_exist.with(:mode, "755") + end + + it 'creates the logs directory' do + directory(node['apache']['log_dir']).must_exist + end + + it 'enables the default site unless it is disabled' do + skip unless node['apache']['default_site_enabled'] + file("#{node['apache']['dir']}/sites-enabled/000-default").must_exist + file("#{node['apache']['dir']}/sites-available/default").must_exist + end + + it 'ensures the debian-style apache module scripts are present' do + %w{a2ensite a2dissite a2enmod a2dismod}.each do |mod_script| + file("/usr/sbin/#{mod_script}").must_exist + end + end + + it 'reports server name only, not detailed version info' do + assert_match(/^ServerTokens Prod *$/, File.read("#{node['apache']['dir']}/conf.d/security")) + end + + it 'listens on port 80' do + apache_configured_ports.must_include(80) + end + + it 'only listens on port 443 when SSL is enabled' do + unless ran_recipe?('apache2::mod_ssl') + apache_configured_ports.wont_include(443) + end + end + + it 'reports server name only, not detailed version info' do + file("#{node['apache']['dir']}/conf.d/security").must_match(/^ServerTokens Prod *$/) + end + + it "enables default_modules" do + node['apache']['default_modules'].each do |a2mod| + apache_enabled_modules.must_include "#{a2mod}_module" + end + end + + describe 'centos' do + it 'ensures no modules are loaded in conf.d' do + Dir["#{node['apache']['dir']}/conf.d/*"].each do |f| + file(f).wont_include 'LoadModule' + end + end + end + + describe 'configuration' do + it { config.must_include '# Generated by Chef' } + it { config.must_include %Q{ServerRoot "#{node['apache']['dir']}"} } + it { config.must_include "Include #{node['apache']['dir']}/conf.d/" } + it { apache_config_parses? } + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb new file mode 100644 index 0000000..5c0d7a0 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/god_monitor_test.rb @@ -0,0 +1,34 @@ +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apache2::god_monitor" do + include Helpers::Apache + + it 'starts god service to supervise apache2' do + service("god").must_be_running + end + + it 'creates the god service template for apache' do + file("/etc/god/conf.d/apache2.god").must_exist + end + + it 'starts an apache2 service that works like a regular service' do + # to be implemented when COOK-744 is fixed + end +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb new file mode 100644 index 0000000..8679b4e --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_apreq2_test.rb @@ -0,0 +1,19 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_apreq2' do + include Helpers::Apache + + it 'enables apreq_module' do + apache_enabled_modules.must_include "apreq_module" + end + + it 'symlinks the module on EL' do + skip unless %w{rhel fedora}.include?(node['platform_family']) + libdir = node['kernel']['machine'] == 'x86_64' ? "lib64" : "lib" + link( + "/usr/#{libdir}/httpd/modules/mod_apreq.so" + ).must_exist.with( + :link_type, :symbolic).and(:to, "/usr/#{libdir}/httpd/modules/mod_apreq2.so" + ) + end +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb new file mode 100644 index 0000000..f7e06ea --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_cas_test.rb @@ -0,0 +1,11 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe "apache2::mod_auth_cas" do + include Helpers::Apache + + it 'enables auth_cas_module' do + skip if %w{rhel fedora}.include?(node['platform_family']) && node['platform_version'].to_f > 6.0 + apache_enabled_modules.must_include "auth_cas_module" + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb new file mode 100644 index 0000000..7793a44 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_auth_openid_test.rb @@ -0,0 +1,37 @@ +require File.expand_path('../support/helpers', __FILE__) +require 'pathname' + +describe 'apache2::mod_auth_openid' do + include Helpers::Apache + + it "installs the opekele library" do + lib_dir = Pathname.new(node['apache']['lib_dir']).dirname.to_s + file("#{lib_dir}/libopkele.so").must_exist + end + + it "does not add the module to httpd.conf" do + conffile = case node['platform'] + when 'debian', 'ubuntu' + "apache2.conf" + when "redhat", "centos", "scientific", "fedora", "arch", "amazon" + "conf/httpd.conf" + when "freebsd" + "httpd.conf" + end + httpd_config = File.read(File.join(node['apache']['dir'], conffile)) + refute_match /^LoadModule authopenid_module /, httpd_config + end + + it "creates a cache directory for the module" do + directory(node['apache']['mod_auth_openid']['cache_dir']).must_exist.with(:owner, node['apache']['user']) + end + + it "ensures the db file is writable by apache" do + file(node['apache']['mod_auth_openid']['dblocation']).must_exist.with(:owner, node['apache']['user']).and(:mode, "644") + end + + it 'enables authopenid_module' do + apache_enabled_modules.must_include "authopenid_module" + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb new file mode 100644 index 0000000..1391240 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_cgi_test.rb @@ -0,0 +1,13 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_cgi' do + include Helpers::Apache + + # the cgi module can be either cgi or cgid + it 'enables cgi or cgid_module' do + assert(apache_enabled_modules.include?('cgi_module') || + apache_enabled_modules.include?('cgid_module') + ) + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb new file mode 100644 index 0000000..cfc1f61 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_dav_svn_test.rb @@ -0,0 +1,14 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_dav_svn' do + include Helpers::Apache + + it 'enables dav_svn_module' do + apache_enabled_modules.must_include "dav_svn_module" + end + + it 'enables dav_module' do + apache_enabled_modules.must_include "dav_module" + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb new file mode 100644 index 0000000..1c404f8 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_fastcgi.rb @@ -0,0 +1,11 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe "apache2::mod_fastcgi" do + include Helpers::Apache + + it 'enables fastcgi_module' do + skip if %w{rhel fedora}.include?(node['platform_family']) + apache_enabled_modules.must_include "fastcgi_module" + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb new file mode 100644 index 0000000..c0c4944 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_include_test.rb @@ -0,0 +1,15 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_include' do + include Helpers::Apache + + it 'enables include_module' do + apache_enabled_modules.must_include "include_module" + end + + it 'drops off the include module configuration' do + assert_match(/AddType text\/html .shtml/, File.read("#{node['apache']['dir']}/mods-enabled/include.conf")) + assert_match(/AddOutputFilter INCLUDES .shtml/, File.read("#{node['apache']['dir']}/mods-enabled/include.conf")) + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb new file mode 100644 index 0000000..c51341a --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_perl_test.rb @@ -0,0 +1,18 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_perl' do + include Helpers::Apache + + it 'enables perl_module' do + apache_enabled_modules.must_include "perl_module" + end + + it 'installs the apache request library' do + req_pkg = case node['platform'] + when 'debian', 'ubuntu' then 'libapache2-request-perl' + else 'perl-libapreq2' + end + package(req_pkg).must_be_installed + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb new file mode 100644 index 0000000..33220b9 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_php5_test.rb @@ -0,0 +1,13 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_php5' do + include Helpers::Apache + + it 'enables php5_module' do + apache_enabled_modules.must_include "php5_module" + end + + it "deletes the packaged php config if any" do + file("#{node['apache']['dir']}/conf.d/php.conf").wont_exist + end +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb new file mode 100644 index 0000000..5b5f2b7 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_python_test.rb @@ -0,0 +1,10 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_python' do + include Helpers::Apache + + it 'enables python_module' do + apache_enabled_modules.must_include "python_module" + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb b/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb new file mode 100644 index 0000000..035f652 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/mod_ssl_test.rb @@ -0,0 +1,28 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'apache2::mod_ssl' do + include Helpers::Apache + + it 'installs the mod_ssl package on RHEL distributions' do + skip unless ["rhel", "fedora"].include? node['platform_family'] + package("mod_ssl").must_be_installed + end + + it 'enables ssl_module' do + apache_enabled_modules.must_include "ssl_module" + end + + it 'does not store SSL config in conf.d' do + file("#{node['apache']['dir']}/conf.d/ssl.conf").wont_exist + end + + it "is configured to listen on port 443" do + apache_configured_ports.must_include(443) + end + + it 'configures SSLCiphersuit from an attribute' do + assert_match(/^SSLCipherSuite #{node['apache']['mod_ssl']['cipher_suite']}$/, + File.read("#{node['apache']['dir']}/mods-enabled/ssl.conf")) + end + +end diff --git a/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..707fcf2 --- /dev/null +++ b/chef/cookbooks/apache2/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,65 @@ +module Helpers + module Apache + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + def apache_config_parses? + acp = shell_out("#{node['apache']['binary']} -t") + acp.exitstatus == 0 + end + + def apache_configured_ports + port_config = File.read("#{node['apache']['dir']}/ports.conf") + port_config.scan(/^Listen ([0-9]+)/).flatten.map { |p| p.to_i } + end + + def apache_enabled_modules + apache_modules = shell_out("#{node['apache']['binary']} -M") + apache_modules.send( + if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 6.0 + :stderr + else + :stdout + end + ).split.select! { |i| i =~ /_module$/ } + end + + def apache_service + service( + case node['platform'] + when "debian", "ubuntu" then "apache2" + when "freebsd" then "apache22" + else "httpd" + end + ) + end + + def config + file( + case node['platform'] + when "debian", "ubuntu" then "#{node['apache']['dir']}/apache2.conf" + when "freebsd" then "#{node['apache']['dir']}/httpd.conf" + else "#{node['apache']['dir']}/conf/httpd.conf" + end + ) + end + + def ran_recipe?(recipe) + if Chef::VERSION < "11.0" + seen_recipes = node.run_state[:seen_recipes] + recipes = seen_recipes.keys.each { |i| i } + else + recipes = run_context.loaded_recipes + end + if recipes.empty? and Chef::Config[:solo] + #If you have roles listed in your run list they are NOT expanded + recipes = node.run_list.map {|item| item.name if item.type == :recipe } + end + recipes.include?(recipe) + end + + end +end diff --git a/chef/cookbooks/apache2/metadata.rb b/chef/cookbooks/apache2/metadata.rb new file mode 100644 index 0000000..a5aa510 --- /dev/null +++ b/chef/cookbooks/apache2/metadata.rb @@ -0,0 +1,213 @@ +name "apache2" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.7.0" +recipe "apache2", "Main Apache configuration" +recipe "apache2::logrotate", "Rotate apache2 logs. Requires logrotate cookbook" +recipe "apache2::mod_alias", "Apache module 'alias' with config file" +recipe "apache2::mod_apreq2", "Apache module 'apreq'" +recipe "apache2::mod_auth_basic", "Apache module 'auth_basic'" +recipe "apache2::mod_auth_digest", "Apache module 'auth_digest'" +recipe "apache2::mod_auth_openid", "Apache module 'authopenid'" +recipe "apache2::mod_authn_file", "Apache module 'authn_file'" +recipe "apache2::mod_authnz_ldap", "Apache module 'authnz_ldap'" +recipe "apache2::mod_authz_default", "Apache module 'authz_default'" +recipe "apache2::mod_authz_groupfile", "Apache module 'authz_groupfile'" +recipe "apache2::mod_authz_host", "Apache module 'authz_host'" +recipe "apache2::mod_authz_user", "Apache module 'authz_user'" +recipe "apache2::mod_autoindex", "Apache module 'autoindex' with config file" +recipe "apache2::mod_cgi", "Apache module 'cgi'" +recipe "apache2::mod_dav", "Apache module 'dav'" +recipe "apache2::mod_dav_svn", "Apache module 'dav_svn'" +recipe "apache2::mod_deflate", "Apache module 'deflate' with config file" +recipe "apache2::mod_dir", "Apache module 'dir' with config file" +recipe "apache2::mod_env", "Apache module 'env'" +recipe "apache2::mod_expires", "Apache module 'expires'" +recipe "apache2::mod_fcgid", "Apache module 'fcgid', package on ubuntu/debian, rhel/centos, compile source on suse; with config file" +recipe "apache2::mod_headers", "Apache module 'headers'" +recipe "apache2::mod_include", "Apache module 'include'" +recipe "apache2::mod_ldap", "Apache module 'ldap'" +recipe "apache2::mod_log_config", "Apache module 'log_config'" +recipe "apache2::mod_mime", "Apache module 'mime' with config file" +recipe "apache2::mod_negotiation", "Apache module 'negotiation' with config file" +recipe "apache2::mod_perl", "Apache module 'perl'" +recipe "apache2::mod_php5", "Apache module 'php5'" +recipe "apache2::mod_proxy", "Apache module 'proxy' with config file" +recipe "apache2::mod_proxy_ajp", "Apache module 'proxy_ajp'" +recipe "apache2::mod_proxy_balancer", "Apache module 'proxy_balancer'" +recipe "apache2::mod_proxy_connect", "Apache module 'proxy_connect'" +recipe "apache2::mod_proxy_http", "Apache module 'proxy_http'" +recipe "apache2::mod_python", "Apache module 'python'" +recipe "apache2::mod_rewrite", "Apache module 'rewrite'" +recipe "apache2::mod_setenvif", "Apache module 'setenvif' with config file" +recipe "apache2::mod_ssl", "Apache module 'ssl' with config file, adds port 443 to listen_ports" +recipe "apache2::mod_status", "Apache module 'status' with config file" +recipe "apache2::mod_xsendfile", "Apache module 'xsendfile'" + +%w{redhat centos scientific fedora debian ubuntu arch freebsd amazon}.each do |os| + supports os +end + +attribute "apache", + :display_name => "Apache Hash", + :description => "Hash of Apache attributes", + :type => "hash" + +attribute "apache/dir", + :display_name => "Apache Directory", + :description => "Location for Apache configuration", + :default => "/etc/apache2" + +attribute "apache/log_dir", + :display_name => "Apache Log Directory", + :description => "Location for Apache logs", + :default => "/etc/apache2" + +attribute "apache/user", + :display_name => "Apache User", + :description => "User Apache runs as", + :default => "www-data" + +attribute "apache/binary", + :display_name => "Apache Binary", + :description => "Apache server daemon program", + :default => "/usr/sbin/apache2" + +attribute "apache/icondir", + :display_name => "Apache Icondir", + :description => "Directory location for icons", + :default => "/usr/share/apache2/icons" + +attribute "apache/listen_ports", + :display_name => "Apache Listen Ports", + :description => "Ports that Apache should listen on", + :type => "array", + :default => ["80", "443"] + +attribute "apache/contact", + :display_name => "Apache Contact", + :description => "Email address of webmaster", + :default => "ops@example.com" + +attribute "apache/timeout", + :display_name => "Apache Timeout", + :description => "Connection timeout value", + :default => "300" + +attribute "apache/keepalive", + :display_name => "Apache Keepalive", + :description => "HTTP persistent connections", + :default => "On" + +attribute "apache/keepaliverequests", + :display_name => "Apache Keepalive Requests", + :description => "Number of requests allowed on a persistent connection", + :default => "100" + +attribute "apache/keepalivetimeout", + :display_name => "Apache Keepalive Timeout", + :description => "Time to wait for requests on persistent connection", + :default => "5" + +attribute "apache/servertokens", + :display_name => "Apache Server Tokens", + :description => "Server response header", + :default => "Prod" + +attribute "apache/serversignature", + :display_name => "Apache Server Signature", + :description => "Configure footer on server-generated documents", + :default => "On" + +attribute "apache/traceenable", + :display_name => "Apache Trace Enable", + :description => "Determine behavior of TRACE requests", + :default => "On" + +attribute "apache/allowed_openids", + :display_name => "Apache Allowed OpenIDs", + :description => "Array of OpenIDs allowed to authenticate", + :default => "" + +attribute "apache/prefork", + :display_name => "Apache Prefork", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/prefork/startservers", + :display_name => "Apache Prefork MPM StartServers", + :description => "Number of MPM servers to start", + :default => "16" + +attribute "apache/prefork/minspareservers", + :display_name => "Apache Prefork MPM MinSpareServers", + :description => "Minimum number of spare server processes", + :default => "16" + +attribute "apache/prefork/maxspareservers", + :display_name => "Apache Prefork MPM MaxSpareServers", + :description => "Maximum number of spare server processes", + :default => "32" + +attribute "apache/prefork/serverlimit", + :display_name => "Apache Prefork MPM ServerLimit", + :description => "Upper limit on configurable server processes", + :default => "400" + +attribute "apache/prefork/maxclients", + :display_name => "Apache Prefork MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "400" + +attribute "apache/prefork/maxrequestsperchild", + :display_name => "Apache Prefork MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "10000" + +attribute "apache/worker", + :display_name => "Apache Worker", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/worker/startservers", + :display_name => "Apache Worker MPM StartServers", + :description => "Initial number of server processes to start", + :default => "4" + +attribute "apache/worker/maxclients", + :display_name => "Apache Worker MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "1024" + +attribute "apache/worker/minsparethreads", + :display_name => "Apache Worker MPM MinSpareThreads", + :description => "Minimum number of spare worker threads", + :default => "64" + +attribute "apache/worker/maxsparethreads", + :display_name => "Apache Worker MPM MaxSpareThreads", + :description => "Maximum number of spare worker threads", + :default => "192" + +attribute "apache/worker/threadsperchild", + :display_name => "Apache Worker MPM ThreadsPerChild", + :description => "Constant number of worker threads in each server process", + :default => "64" + +attribute "apache/worker/maxrequestsperchild", + :display_name => "Apache Worker MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "0" + +attribute "apache/default_modules", + :display_name => "Apache Default Modules", + :description => "Default modules to enable via recipes", + :default => "status alias auth_basic authn_file authz_default authz_groupfile authz_host authz_user autoindex dir env mime negotiation setenvif" + +attribute "apache/mod_ssl/cipher_suite", + :display_name => "Apache mod_ssl Cipher Suite", + :description => "String of SSL ciphers to use for SSLCipherSuite", + :default => "RC4-SHA:HIGH:!ADH" diff --git a/chef/cookbooks/apache2/recipes/default.rb b/chef/cookbooks/apache2/recipes/default.rb new file mode 100644 index 0000000..f5cb6e6 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/default.rb @@ -0,0 +1,223 @@ +# +# Cookbook Name:: apache2 +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "apache2" do + package_name node['apache']['package'] +end + +service "apache2" do + case node['platform_family'] + when "rhel", "fedora", "suse" + service_name "httpd" + # If restarted/reloaded too quickly httpd has a habit of failing. + # This may happen with multiple recipes notifying apache to restart - like + # during the initial bootstrap. + restart_command "/sbin/service httpd restart && sleep 1" + reload_command "/sbin/service httpd reload && sleep 1" + when "debian" + service_name "apache2" + restart_command "/usr/sbin/invoke-rc.d apache2 restart && sleep 1" + reload_command "/usr/sbin/invoke-rc.d apache2 reload && sleep 1" + when "arch" + service_name "httpd" + when "freebsd" + service_name "apache22" + end + supports [:restart, :reload, :status] + action :enable +end + +if platform_family?("rhel", "fedora", "arch", "suse", "freebsd") + directory node['apache']['log_dir'] do + mode 00755 + end + + package "perl" + + cookbook_file "/usr/local/bin/apache2_module_conf_generate.pl" do + source "apache2_module_conf_generate.pl" + mode 00755 + owner "root" + group node['apache']['root_group'] + end + + %w{sites-available sites-enabled mods-available mods-enabled}.each do |dir| + directory "#{node['apache']['dir']}/#{dir}" do + mode 00755 + owner "root" + group node['apache']['root_group'] + end + end + + execute "generate-module-list" do + command "/usr/local/bin/apache2_module_conf_generate.pl #{node['apache']['lib_dir']} #{node['apache']['dir']}/mods-available" + action :nothing + end + + %w{a2ensite a2dissite a2enmod a2dismod}.each do |modscript| + template "/usr/sbin/#{modscript}" do + source "#{modscript}.erb" + mode 00700 + owner "root" + group node['apache']['root_group'] + end + end + + # installed by default on centos/rhel, remove in favour of mods-enabled + %w{ proxy_ajp auth_pam authz_ldap webalizer ssl welcome }.each do |f| + file "#{node['apache']['dir']}/conf.d/#{f}.conf" do + action :delete + backup false + end + end + + # installed by default on centos/rhel, remove in favour of mods-enabled + file "#{node['apache']['dir']}/conf.d/README" do + action :delete + backup false + end + + # enable mod_deflate for consistency across distributions + include_recipe "apache2::mod_deflate" +end + +if platform_family?("freebsd") + + file "#{node['apache']['dir']}/Includes/no-accf.conf" do + action :delete + backup false + end + + directory "#{node['apache']['dir']}/Includes" do + action :delete + end + + %w{ + httpd-autoindex.conf httpd-dav.conf httpd-default.conf httpd-info.conf + httpd-languages.conf httpd-manual.conf httpd-mpm.conf + httpd-multilang-errordoc.conf httpd-ssl.conf httpd-userdir.conf + httpd-vhosts.conf + }.each do |f| + + file "#{node['apache']['dir']}/extra/#{f}" do + action :delete + backup false + end + + end + + directory "#{node['apache']['dir']}/extra" do + action :delete + end + +end + +directory "#{node['apache']['dir']}/ssl" do + mode 00755 + owner "root" + group node['apache']['root_group'] +end + +directory "#{node['apache']['dir']}/conf.d" do + mode 00755 + owner "root" + group node['apache']['root_group'] +end + +directory node['apache']['cache_dir'] do + mode 00755 + owner "root" + group node['apache']['root_group'] +end + +# Set the preferred execution binary - prefork or worker +template "/etc/sysconfig/httpd" do + source "etc-sysconfig-httpd.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 + notifies :restart, "service[apache2]" + only_if { platform_family?("rhel", "fedora") } +end + +template "apache2.conf" do + case node['platform_family'] + when "rhel", "fedora", "arch" + path "#{node['apache']['dir']}/conf/httpd.conf" + when "debian" + path "#{node['apache']['dir']}/apache2.conf" + when "freebsd" + path "#{node['apache']['dir']}/httpd.conf" + end + source "apache2.conf.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 + notifies :restart, "service[apache2]" +end + +template "apache2-conf-security" do + path "#{node['apache']['dir']}/conf.d/security" + source "security.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 + backup false + notifies :restart, "service[apache2]" +end + +template "apache2-conf-charset" do + path "#{node['apache']['dir']}/conf.d/charset" + source "charset.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 + backup false + notifies :restart, "service[apache2]" +end + +template "#{node['apache']['dir']}/ports.conf" do + source "ports.conf.erb" + owner "root" + group node['apache']['root_group'] + variables :apache_listen_ports => node['apache']['listen_ports'].map { |p| p.to_i }.uniq + mode 00644 + notifies :restart, "service[apache2]" +end + +template "#{node['apache']['dir']}/sites-available/default" do + source "default-site.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 + notifies :restart, "service[apache2]" +end + +node['apache']['default_modules'].each do |mod| + module_recipe_name = mod =~ /^mod_/ ? mod : "mod_#{mod}" + include_recipe "apache2::#{module_recipe_name}" +end + +apache_site "default" do + enable node['apache']['default_site_enabled'] +end + +service "apache2" do + action :start +end diff --git a/chef/cookbooks/apache2/recipes/god_monitor.rb b/chef/cookbooks/apache2/recipes/god_monitor.rb new file mode 100644 index 0000000..b3c49eb --- /dev/null +++ b/chef/cookbooks/apache2/recipes/god_monitor.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: apache2 +# Recipe:: god_monitor +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_service = service "apache2" do + action :nothing +end + +start_command = apache_service.start_command +stop_command = apache_service.stop_command +restart_command = apache_service.restart_command + +god_monitor "apache2" do + config "apache2.god.erb" + start (start_command)?start_command : "/etc/init.d/#{apache_service.service_name} start" + restart (restart_command)?restart_command : "/etc/init.d/#{apache_service.service_name} restart" + stop (stop_command)?stop_command : "/etc/init.d/#{apache_service.service_name} stop" +end diff --git a/chef/cookbooks/apache2/recipes/iptables.rb b/chef/cookbooks/apache2/recipes/iptables.rb new file mode 100644 index 0000000..fbf1eba --- /dev/null +++ b/chef/cookbooks/apache2/recipes/iptables.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: iptables +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +iptables_rule "port_apache" diff --git a/chef/cookbooks/apache2/recipes/logrotate.rb b/chef/cookbooks/apache2/recipes/logrotate.rb new file mode 100644 index 0000000..d90b0fb --- /dev/null +++ b/chef/cookbooks/apache2/recipes/logrotate.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: apache2 +# Recipe:: logrotate +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_service = service "apache2" do + action :nothing +end + +begin + include_recipe 'logrotate' +rescue + Chef::Log.warn("The apache::logrotate recipe requires the logrotate cookbook. Install the cookbook with `knife cookbook site install logrotate`.") +end +logrotate_app apache_service.service_name do + path node['apache']['log_dir'] +end diff --git a/chef/cookbooks/apache2/recipes/mod_actions.rb b/chef/cookbooks/apache2/recipes/mod_actions.rb new file mode 100644 index 0000000..da13b77 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_actions.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: actions +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "actions" diff --git a/chef/cookbooks/apache2/recipes/mod_alias.rb b/chef/cookbooks/apache2/recipes/mod_alias.rb new file mode 100644 index 0000000..a4618ed --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_alias.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: alias +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "alias" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_apreq2.rb b/chef/cookbooks/apache2/recipes/mod_apreq2.rb new file mode 100644 index 0000000..c13ca10 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_apreq2.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: apache2 +# Recipe:: apreq2 +# +# modified from the python recipe by Jeremy Bingham +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2" + +case node['platform_family'] +when "debian" + + package "libapache2-mod-apreq2" + +when "rhel", "fedora" + + package "libapreq2" do + notifies :run, "execute[generate-module-list]", :immediately + end + + # seems that the apreq lib is weirdly broken or something - it needs to be + # loaded as "apreq", but on RHEL & derivitatives the file needs a symbolic + # link to mod_apreq.so. + link "/usr/lib64/httpd/modules/mod_apreq.so" do + to "/usr/lib64/httpd/modules/mod_apreq2.so" + only_if "test -f /usr/lib64/httpd/modules/mod_apreq2.so" + end + + link "/usr/lib/httpd/modules/mod_apreq.so" do + to "/usr/lib/httpd/modules/mod_apreq2.so" + only_if "test -f /usr/lib/httpd/modules/mod_apreq2.so" + end +end + +file "#{node['apache']['dir']}/conf.d/apreq.conf" do + action :delete + backup false +end + +apache_module "apreq" diff --git a/chef/cookbooks/apache2/recipes/mod_auth_basic.rb b/chef/cookbooks/apache2/recipes/mod_auth_basic.rb new file mode 100644 index 0000000..d30264f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_auth_basic.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_basic +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_basic" diff --git a/chef/cookbooks/apache2/recipes/mod_auth_cas.rb b/chef/cookbooks/apache2/recipes/mod_auth_cas.rb new file mode 100644 index 0000000..87096d7 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_auth_cas.rb @@ -0,0 +1,59 @@ +include_recipe "apache2" + +if node['apache']['mod_auth_cas']['from_source'] + + package "httpd-devel" do + package_name value_for_platform_family( + ["rhel", "fedora", "suse"] => "httpd-devel", + "debian" => "apache2-dev" + ) + end + + git '/tmp/mod_auth_cas' do + repository 'git://github.com/Jasig/mod_auth_cas.git' + revision node['apache']['mod_auth_cas']['source_revision'] + notifies :run, 'execute[compile mod_auth_cas]', :immediately + end + + execute 'compile mod_auth_cas' do + command './configure && make && make install' + cwd '/tmp/mod_auth_cas' + not_if "test -f #{node['apache']['libexecdir']}/mod_auth_cas.so" + end + + template "#{node['apache']['dir']}/mods-available/auth_cas.load" do + source 'mods/auth_cas.load.erb' + owner 'root' + group node['apache']['root_group'] + mode 00644 + end + +else + case node['platform_family'] + when "debian" + + package "libapache2-mod-auth-cas" + + when "rhel", "fedora" + + yum_package "mod_auth_cas" do + notifies :run, "execute[generate-module-list]", :immediately + end + + file "#{node['apache']['dir']}/conf.d/auth_cas.conf" do + action :delete + backup false + end + + end +end + +apache_module 'auth_cas' do + conf true +end + +directory "#{node['apache']['cache_dir']}/mod_auth_cas" do + owner node['apache']['user'] + group node['apache']['group'] + mode 00700 +end diff --git a/chef/cookbooks/apache2/recipes/mod_auth_digest.rb b/chef/cookbooks/apache2/recipes/mod_auth_digest.rb new file mode 100644 index 0000000..5aef926 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_auth_digest.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_digest +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_digest" diff --git a/chef/cookbooks/apache2/recipes/mod_auth_openid.rb b/chef/cookbooks/apache2/recipes/mod_auth_openid.rb new file mode 100644 index 0000000..2aa6614 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_auth_openid.rb @@ -0,0 +1,125 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mod_auth_openid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +openid_dev_pkgs = value_for_platform_family( + ["debian"] => %w{automake make g++ apache2-prefork-dev libopkele-dev libopkele3 libtool}, + ["rhel", "fedora"] => %w{gcc-c++ httpd-devel curl-devel libtidy libtidy-devel sqlite-devel pcre-devel openssl-devel make libtool}, + "arch" => ["libopkele"], + "freebsd" => %w{libopkele pcre sqlite3} +) + +make_cmd = value_for_platform_family( + "freebsd" => { "default" => "gmake" }, + "default" => "make" +) + +case node['platform_family'] +when "arch" + + include_recipe "pacman" + package "tidyhtml" + pacman_aur openid_dev_pkgs.first do + action [:build, :install] + end + +else + openid_dev_pkgs.each do |pkg| + + package pkg + + end +end + +case node['platform_family'] +when "rhel", "fedora" + remote_file "#{Chef::Config['file_cache_path']}/libopkele-2.0.4.tar.gz" do + source "http://kin.klever.net/dist/libopkele-2.0.4.tar.gz" + mode 00644 + checksum "57a5bc753b7e80c5ece1e5968b2051b0ce7ed9ce4329d17122c61575a9ea7648" + end + + bash "install libopkele" do + cwd Chef::Config['file_cache_path'] + # Ruby 1.8.6 does not have rpartition, unfortunately + syslibdir = node['apache']['lib_dir'][0..node['apache']['lib_dir'].rindex("/")] + code <<-EOH + tar zxvf libopkele-2.0.4.tar.gz + cd libopkele-2.0.4 && ./configure --prefix=/usr --libdir=#{syslibdir} + #{make_cmd} && #{make_cmd} install + EOH + creates "#{syslibdir}/libopkele.a" + end +end + +version = node['apache']['mod_auth_openid']['ref'] +configure_flags = node['apache']['mod_auth_openid']['configure_flags'] + +remote_file "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}.tar.gz" do + source node['apache']['mod_auth_openid']['source_url'] + mode 00644 + action :create_if_missing +end + +file "mod_auth_openid_dblocation" do + path node['apache']['mod_auth_openid']['dblocation'] + action :nothing +end + +bash "untar mod_auth_openid" do + cwd Chef::Config['file_cache_path'] + code <<-EOH + tar zxvf mod_auth_openid-#{version}.tar.gz + EOH +end + +bash "compile mod_auth_openid" do + cwd "#{Chef::Config['file_cache_path']}/mod_auth_openid-#{version}" + code <<-EOH + ./autogen.sh + ./configure #{configure_flags.join(' ')} + perl -pi -e "s/-i -a -n 'authopenid'/-i -n 'authopenid'/g" Makefile + #{make_cmd} && #{make_cmd} install + EOH + creates "#{node['apache']['libexecdir']}/mod_auth_openid.so" + notifies :delete, "file[mod_auth_openid_dblocation]", :immediately + notifies :restart, "service[apache2]" +end + +directory node['apache']['mod_auth_openid']['cache_dir'] do + owner node['apache']['user'] + group node['apache']['group'] + mode 00700 +end + +file node['apache']['mod_auth_openid']['dblocation'] do + owner node['apache']['user'] + group node['apache']['group'] + mode 00644 +end + +template "#{node['apache']['dir']}/mods-available/authopenid.load" do + source "mods/authopenid.load.erb" + owner "root" + group node['apache']['root_group'] + mode 00644 +end + +apache_module "authopenid" do + filename "mod_auth_openid.so" +end diff --git a/chef/cookbooks/apache2/recipes/mod_authn_file.rb b/chef/cookbooks/apache2/recipes/mod_authn_file.rb new file mode 100644 index 0000000..872caa7 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authn_file.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authn_file +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authn_file" diff --git a/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb b/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb new file mode 100644 index 0000000..0310d24 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authnz_ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authnz_ldap" diff --git a/chef/cookbooks/apache2/recipes/mod_authz_default.rb b/chef/cookbooks/apache2/recipes/mod_authz_default.rb new file mode 100644 index 0000000..123536d --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authz_default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_default" diff --git a/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb b/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb new file mode 100644 index 0000000..b2833b2 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_groupfile +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_groupfile" diff --git a/chef/cookbooks/apache2/recipes/mod_authz_host.rb b/chef/cookbooks/apache2/recipes/mod_authz_host.rb new file mode 100644 index 0000000..87c1a4b --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authz_host.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_host +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_host" diff --git a/chef/cookbooks/apache2/recipes/mod_authz_user.rb b/chef/cookbooks/apache2/recipes/mod_authz_user.rb new file mode 100644 index 0000000..8dd46df --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_authz_user.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_user +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_user" diff --git a/chef/cookbooks/apache2/recipes/mod_autoindex.rb b/chef/cookbooks/apache2/recipes/mod_autoindex.rb new file mode 100644 index 0000000..622a66e --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_autoindex.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: autoindex +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "autoindex" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_cgi.rb b/chef/cookbooks/apache2/recipes/mod_cgi.rb new file mode 100644 index 0000000..a151d07 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_cgi.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: cgi +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "cgi" diff --git a/chef/cookbooks/apache2/recipes/mod_dav.rb b/chef/cookbooks/apache2/recipes/mod_dav.rb new file mode 100644 index 0000000..94f7c1a --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_dav.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dav" diff --git a/chef/cookbooks/apache2/recipes/mod_dav_fs.rb b/chef/cookbooks/apache2/recipes/mod_dav_fs.rb new file mode 100644 index 0000000..bd3e74b --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_dav_fs.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav_fs +# +# Copyright 2011, Atriso +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::mod_dav" +apache_module "dav_fs" diff --git a/chef/cookbooks/apache2/recipes/mod_dav_svn.rb b/chef/cookbooks/apache2/recipes/mod_dav_svn.rb new file mode 100644 index 0000000..3a1d0fc --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_dav_svn.rb @@ -0,0 +1,41 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav_svn +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::mod_dav" + +package "libapache2-svn" do + case node['platform_family'] + when "rhel", "fedora", "suse" + package_name "mod_dav_svn" + else + package_name "libapache2-svn" + end +end + +case node['platform_family'] +when "rhel", "fedora", "suse" + + file "#{node['apache']['conf']}/conf.d/subversion.conf" do + action :delete + backup false + end + +end + +apache_module "dav_svn" diff --git a/chef/cookbooks/apache2/recipes/mod_deflate.rb b/chef/cookbooks/apache2/recipes/mod_deflate.rb new file mode 100644 index 0000000..b568f30 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_deflate.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: deflate +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "deflate" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_dir.rb b/chef/cookbooks/apache2/recipes/mod_dir.rb new file mode 100644 index 0000000..e59b36b --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_dir.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dir +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dir" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_env.rb b/chef/cookbooks/apache2/recipes/mod_env.rb new file mode 100644 index 0000000..d345503 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_env.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: env +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "env" diff --git a/chef/cookbooks/apache2/recipes/mod_expires.rb b/chef/cookbooks/apache2/recipes/mod_expires.rb new file mode 100644 index 0000000..9e5042e --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_expires.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: expires +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "expires" diff --git a/chef/cookbooks/apache2/recipes/mod_fastcgi.rb b/chef/cookbooks/apache2/recipes/mod_fastcgi.rb new file mode 100644 index 0000000..a0a4d95 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_fastcgi.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: apache2 +# Recipe:: fastcgi +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform_family?("debian") + package "libapache2-mod-fastcgi" + + apache_module "fastcgi" do + conf true + end +end diff --git a/chef/cookbooks/apache2/recipes/mod_fcgid.rb b/chef/cookbooks/apache2/recipes/mod_fcgid.rb new file mode 100644 index 0000000..b82dc95 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_fcgid.rb @@ -0,0 +1,55 @@ +# +# Cookbook Name:: apache2 +# Recipe:: fcgid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform_family?("debian") + package "libapache2-mod-fcgid" +elsif platform_family?("rhel", "fedora") + + package "mod_fcgid" do + notifies :run, resources(:execute => "generate-module-list"), :immediately + end + + file "#{node['apache']['dir']}/conf.d/fcgid.conf" do + action :delete + backup false + end + + directory "/var/run/httpd/mod_fcgid" do + recursive true + only_if { node['platform_version'].to_i >= 6 } + end + +elsif platform_family?("suse") + apache_lib_path = node['apache']['lib_dir'] + + package "httpd-devel" + + bash "install-fcgid" do + code <<-EOH +(cd #{Chef::Config['file_cache_path']}; wget http://superb-east.dl.sourceforge.net/sourceforge/mod-fcgid/mod_fcgid.2.2.tgz) +(cd #{Chef::Config['file_cache_path']}; tar zxvf mod_fcgid.2.2.tgz) +(cd #{Chef::Config['file_cache_path']}; perl -pi -e 's!/usr/local/apache2!#{apache_lib_path}!g' ./mod_fcgid.2.2/Makefile) +(cd #{Chef::Config['file_cache_path']}/mod_fcgid.2.2; make install) +EOH + end +end + +apache_module "fcgid" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_filter.rb b/chef/cookbooks/apache2/recipes/mod_filter.rb new file mode 100644 index 0000000..148ef7d --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_filter.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: filter +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "filter" diff --git a/chef/cookbooks/apache2/recipes/mod_headers.rb b/chef/cookbooks/apache2/recipes/mod_headers.rb new file mode 100644 index 0000000..5e6b94d --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_headers.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: headers +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "headers" diff --git a/chef/cookbooks/apache2/recipes/mod_include.rb b/chef/cookbooks/apache2/recipes/mod_include.rb new file mode 100644 index 0000000..e46d81f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_include.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: include +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "include" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_ldap.rb b/chef/cookbooks/apache2/recipes/mod_ldap.rb new file mode 100644 index 0000000..0877694 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "ldap" diff --git a/chef/cookbooks/apache2/recipes/mod_log_config.rb b/chef/cookbooks/apache2/recipes/mod_log_config.rb new file mode 100644 index 0000000..4ab653f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_log_config.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: apache2 +# Recipe:: log_config +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform_family?("rhel", "fedora", "suse", "arch", "freebsd") + apache_module "log_config" +else + include_recipe "apache2" +end diff --git a/chef/cookbooks/apache2/recipes/mod_logio.rb b/chef/cookbooks/apache2/recipes/mod_logio.rb new file mode 100644 index 0000000..74f1350 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_logio.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: apache2 +# Recipe:: logio +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform_family?("rhel", "fedora", "suse", "arch", "freebsd") + apache_module "logio" +else + include_recipe "apache2" +end diff --git a/chef/cookbooks/apache2/recipes/mod_mime.rb b/chef/cookbooks/apache2/recipes/mod_mime.rb new file mode 100644 index 0000000..16aee1a --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_mime.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mime +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "mime" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_negotiation.rb b/chef/cookbooks/apache2/recipes/mod_negotiation.rb new file mode 100644 index 0000000..348e11f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_negotiation.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: negotiation +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "negotiation" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_perl.rb b/chef/cookbooks/apache2/recipes/mod_perl.rb new file mode 100644 index 0000000..b4d2a74 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_perl.rb @@ -0,0 +1,44 @@ +# +# Cookbook Name:: apache2 +# Recipe:: perl +# +# adapted from the mod_python recipe by Jeremy Bingham +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + %w{libapache2-mod-perl2 libapache2-request-perl apache2-mpm-prefork}.each do |pkg| + + package pkg + + end +when "rhel", "fedora" + + package "mod_perl" do + notifies :run, "execute[generate-module-list]", :immediately + end + + package "perl-libapreq2" + +end + +file "#{node['apache']['dir']}/conf.d/perl.conf" do + action :delete + backup false +end + +apache_module "perl" diff --git a/chef/cookbooks/apache2/recipes/mod_php5.rb b/chef/cookbooks/apache2/recipes/mod_php5.rb new file mode 100644 index 0000000..63cc0ce --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_php5.rb @@ -0,0 +1,78 @@ +# +# Cookbook Name:: apache2 +# Recipe:: php5 +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + + package "libapache2-mod-php5" + +when "arch" + + package "php-apache" do + notifies :run, "execute[generate-module-list]", :immediately + end + +when "rhel" + + package "which" + package "php package" do + if node['platform_version'].to_f < 6.0 + package_name "php53" + else + package_name "php" + end + notifies :run, "execute[generate-module-list]", :immediately + not_if "which php" + end + +when "fedora" + + package "php package" do + package_name "php" + notifies :run, "execute[generate-module-list]", :immediately + not_if "which php" + end + +when "freebsd" + + freebsd_port_options "php5" do + options "APACHE" => true + action :create + end + + package "php package" do + package_name "php5" + source "ports" + notifies :run, "execute[generate-module-list]", :immediately + end + +end + +file "#{node['apache']['dir']}/conf.d/php.conf" do + action :delete + backup false +end + +apache_module "php5" do + case node['platform_family'] + when "rhel", "fedora", "freebsd" + conf true + filename "libphp5.so" + end +end diff --git a/chef/cookbooks/apache2/recipes/mod_proxy.rb b/chef/cookbooks/apache2/recipes/mod_proxy.rb new file mode 100644 index 0000000..fff7627 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_proxy.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb b/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb new file mode 100644 index 0000000..61bc078 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::mod_proxy" +apache_module "proxy_ajp" diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb b/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb new file mode 100644 index 0000000..dc62a71 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_balancer" diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb b/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb new file mode 100644 index 0000000..f41954f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_connect" diff --git a/chef/cookbooks/apache2/recipes/mod_proxy_http.rb b/chef/cookbooks/apache2/recipes/mod_proxy_http.rb new file mode 100644 index 0000000..ddff3ea --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_proxy_http.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy_http +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_http" diff --git a/chef/cookbooks/apache2/recipes/mod_python.rb b/chef/cookbooks/apache2/recipes/mod_python.rb new file mode 100644 index 0000000..bfa684a --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_python.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: apache2 +# Recipe:: python +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + + package "libapache2-mod-python" + +when "rhel", "fedora" + + package "mod_python" do + notifies :run, "execute[generate-module-list]", :immediately + end +end + +file "#{node['apache']['dir']}/conf.d/python.conf" do + action :delete + backup false +end + +apache_module "python" diff --git a/chef/cookbooks/apache2/recipes/mod_rewrite.rb b/chef/cookbooks/apache2/recipes/mod_rewrite.rb new file mode 100644 index 0000000..df388a6 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_rewrite.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: rewrite +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "rewrite" diff --git a/chef/cookbooks/apache2/recipes/mod_setenvif.rb b/chef/cookbooks/apache2/recipes/mod_setenvif.rb new file mode 100644 index 0000000..4048a5f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_setenvif.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: setenvif +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "setenvif" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_ssl.rb b/chef/cookbooks/apache2/recipes/mod_ssl.rb new file mode 100644 index 0000000..d5095ec --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_ssl.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ssl +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +unless node['apache']['listen_ports'].include?("443") + node.set['apache']['listen_ports'] = node['apache']['listen_ports'] + ["443"] +end + +ports = node['apache']['listen_ports'] + +if platform_family?("rhel", "fedora", "suse") + + package "mod_ssl" do + notifies :run, "execute[generate-module-list]", :immediately + end + + file "#{node['apache']['dir']}/conf.d/ssl.conf" do + action :delete + backup false + end +end + +template "#{node['apache']['dir']}/ports.conf" do + source "ports.conf.erb" + variables :apache_listen_ports => ports.map { |p| p.to_i }.uniq + notifies :restart, "service[apache2]" + mode 00644 +end + +apache_module "ssl" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_status.rb b/chef/cookbooks/apache2/recipes/mod_status.rb new file mode 100644 index 0000000..3e71727 --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_status.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: status +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "status" do + conf true +end diff --git a/chef/cookbooks/apache2/recipes/mod_wsgi.rb b/chef/cookbooks/apache2/recipes/mod_wsgi.rb new file mode 100644 index 0000000..fef91bb --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_wsgi.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mod_wsgi +# +# Copyright 2008-2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + + package "libapache2-mod-wsgi" + +when "rhel", "fedora", "arch" + + package "mod_wsgi" do + notifies :run, "execute[generate-module-list]", :immediately + end + +end + +file "#{node['apache']['dir']}/conf.d/wsgi.conf" do + action :delete + backup false +end + +apache_module "wsgi" diff --git a/chef/cookbooks/apache2/recipes/mod_xsendfile.rb b/chef/cookbooks/apache2/recipes/mod_xsendfile.rb new file mode 100644 index 0000000..9d4c60f --- /dev/null +++ b/chef/cookbooks/apache2/recipes/mod_xsendfile.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mod_xsendfile +# +# Copyright 2011, CustomInk, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + + package "libapache2-mod-xsendfile" + +when "rhel", "fedora" + + package "mod_xsendfile" do + notifies :run, "execute[generate-module-list]", :immediately + end + +end + +file "#{node['apache']['dir']}/conf.d/xsendfile.conf" do + action :delete + backup false +end + +apache_module "xsendfile" diff --git a/chef/cookbooks/apache2/templates/default/a2dismod.erb b/chef/cookbooks/apache2/templates/default/a2dismod.erb new file mode 100644 index 0000000..e66a292 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/a2dismod.erb @@ -0,0 +1,22 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node['apache']['dir'] %>' + +if [ -z $1 ]; then + echo "Which module would you like to disable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-enabled/*.load | \ + sed -e "s,$SYSCONFDIR/mods-enabled/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +if ! [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load ]; then + echo "This module is already disabled, or does not exist!" + exit 1 +fi + +rm -f $SYSCONFDIR/mods-enabled/$MODNAME.* +echo "Module $MODNAME disabled; reload apache to fully disable." \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/a2dissite.erb b/chef/cookbooks/apache2/templates/default/a2dissite.erb new file mode 100644 index 0000000..9e074c5 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/a2dissite.erb @@ -0,0 +1,29 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node['apache']['dir'] %>' + +if [ -z $1 ]; then + echo "Which site would you like to disable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/sites-enabled/* | \ + sed -e "s,$SYSCONFDIR/sites-enabled/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if ! [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already disabled, or does not exist!" + exit 1 +fi + +if ! rm $SYSCONFDIR/sites-enabled/$SITENAME 2>/dev/null; then + rm -f $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +fi +echo "Site $SITENAME disabled; reload apache to disable." diff --git a/chef/cookbooks/apache2/templates/default/a2enmod.erb b/chef/cookbooks/apache2/templates/default/a2enmod.erb new file mode 100644 index 0000000..fe641df --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/a2enmod.erb @@ -0,0 +1,37 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node['apache']['dir'] %>' + +if [ -z $1 ]; then + echo "Which module would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-available/*.load | \ + sed -e "s,$SYSCONFDIR/mods-available/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +#figure out if we're on a prefork or threaded mpm +if [ -x /usr/sbin/apache2 ]; then + PREFORK=`/usr/sbin/apache2 -l | grep prefork || true` +fi + +if [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load -a -e $SYSCONFDIR/mods-enabled/$MODNAME.conf ]; then + echo "This module is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/mods-available/$MODNAME.load ]; then + echo "This module does not exist!" + exit 1 +fi + +for i in conf load; do + if [ -e $SYSCONFDIR/mods-available/$MODNAME.$i -a ! -e $SYSCONFDIR/mods-enabled/$MODNAME.$i ]; then + ln -sf $SYSCONFDIR/mods-available/$MODNAME.$i $SYSCONFDIR/mods-enabled/$MODNAME.$i; + fi +done + +echo "Module $MODNAME installed; reload apache to enable." diff --git a/chef/cookbooks/apache2/templates/default/a2ensite.erb b/chef/cookbooks/apache2/templates/default/a2ensite.erb new file mode 100644 index 0000000..3a7c628 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/a2ensite.erb @@ -0,0 +1,38 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node['apache']['dir'] %>' + +if [ -z $1 ]; then + echo "Which site would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/sites-available/* | \ + sed -e "s,$SYSCONFDIR/sites-available/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/sites-available/$SITENAME ]; then + echo "This site does not exist!" + exit 1 +fi + +if [ $SITENAME = "default" ]; then + ln -sf $SYSCONFDIR/sites-available/$SITENAME \ + $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +else + ln -sf $SYSCONFDIR/sites-available/$SITENAME $SYSCONFDIR/sites-enabled/$SITENAME +fi + +echo "Site $SITENAME installed; reload apache to enable." \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/apache2.conf.erb b/chef/cookbooks/apache2/templates/default/apache2.conf.erb new file mode 100644 index 0000000..bea4993 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/apache2.conf.erb @@ -0,0 +1,238 @@ +# +# Generated by Chef +# +# Based on the Ubuntu apache2.conf + +ServerRoot "<%= node['apache']['dir'] %>" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +<% if %w{debian}.include?(node['platform_family']) -%> +LockFile /var/lock/apache2/accept.lock +<% elsif %w{freebsd}.include?(node['platform_family']) -%> +LockFile /var/log/accept.lock +<% else %> +LockFile logs/accept.lock +<% end -%> + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# +PidFile <%= node['apache']['pid_file'] %> + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout <%= node['apache']['timeout'] %> + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive <%= node['apache']['keepalive'] %> + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests <%= node['apache']['keepaliverequests'] %> + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout <%= node['apache']['keepalivetimeout'] %> + +## +## Server-Pool Size Regulation (MPM specific) +## + +# prefork MPM +# StartServers: number of server processes to start +# MinSpareServers: minimum number of server processes which are kept spare +# MaxSpareServers: maximum number of server processes which are kept spare +# MaxClients: maximum number of server processes allowed to start +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= node['apache']['prefork']['startservers'] %> + MinSpareServers <%= node['apache']['prefork']['minspareservers'] %> + MaxSpareServers <%= node['apache']['prefork']['maxspareservers'] %> + ServerLimit <%= node['apache']['prefork']['serverlimit'] %> + MaxClients <%= node['apache']['prefork']['maxclients'] %> + MaxRequestsPerChild <%= node['apache']['prefork']['maxrequestsperchild'] %> + + +# worker MPM +# StartServers: initial number of server processes to start +# MaxClients: maximum number of simultaneous client connections +# MinSpareThreads: minimum number of worker threads which are kept spare +# MaxSpareThreads: maximum number of worker threads which are kept spare +# ThreadsPerChild: constant number of worker threads in each server process +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= node['apache']['worker']['startservers'] %> + ServerLimit <%= node['apache']['worker']['serverlimit'] %> + MaxClients <%= node['apache']['worker']['maxclients'] %> + MinSpareThreads <%= node['apache']['worker']['minsparethreads'] %> + MaxSpareThreads <%= node['apache']['worker']['maxsparethreads'] %> + ThreadsPerChild <%= node['apache']['worker']['threadsperchild'] %> + MaxRequestsPerChild <%= node['apache']['worker']['maxrequestsperchild'] %> + + +User <%= node['apache']['user'] %> +Group <%= node['apache']['group'] %> + +# +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# + +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Order allow,deny + Deny from all + + +# +# DefaultType is the default MIME type the server will use for a document +# if it cannot otherwise determine one, such as from filename extensions. +# If your server contains mostly text or HTML documents, "text/plain" is +# a good value. If most of your content is binary, such as applications +# or images, you may want to use "application/octet-stream" instead to +# keep browsers from trying to display binary files as though they are +# text. +# +DefaultType text/plain + + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog <%= node['apache']['log_dir'] %>/<%= node['apache']['error_log'] %> + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + +# COOK-1021: Dummy LoadModule directive to aid module installations +#LoadModule dummy_module modules/mod_dummy.so + +# Include module configuration: +Include <%= node['apache']['dir'] %>/mods-enabled/*.load +Include <%= node['apache']['dir'] %>/mods-enabled/*.conf + +<% if %w{freebsd}.include?(node['platform_family']) -%> + + AcceptFilter http none + AcceptFilter https none + +<% end %> + +# Include ports listing +Include <%= node['apache']['dir'] %>/ports.conf + +# +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +# +LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent +# + +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# Putting this all together, we can internationalize error responses. +# +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line: +# +# Alias /error/include/ "/your/include/path/" +# +# which allows you to create your own set of files by starting with the +# /usr/share/apache2/error/include/ files and copying them to /your/include/path/, +# even on a per-VirtualHost basis. The default include files will display +# your Apache version number and your ServerAdmin email address regardless +# of the setting of ServerSignature. +# +# The internationalized error documents require mod_alias, mod_include +# and mod_negotiation. To activate them, uncomment the following 30 lines. + +# Alias /error/ "/usr/share/apache2/error/" +# +# +# AllowOverride None +# Options IncludesNoExec +# AddOutputFilter Includes html +# AddHandler type-map var +# Order allow,deny +# Allow from all +# LanguagePriority en cs de es fr it nl sv pt-br ro +# ForceLanguagePriority Prefer Fallback +# +# +# ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +# ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +# ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +# ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +# ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +# ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +# ErrorDocument 410 /error/HTTP_GONE.html.var +# ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +# ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +# ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +# ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +# ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +# ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +# ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +# ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +# ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +# ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + + +# Include generic snippets of statements +Include <%= node['apache']['dir'] %>/conf.d/ + +# Include the virtual host configurations: +Include <%= node['apache']['dir'] %>/sites-enabled/ diff --git a/chef/cookbooks/apache2/templates/default/apache2.god.erb b/chef/cookbooks/apache2/templates/default/apache2.god.erb new file mode 100644 index 0000000..86947f5 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/apache2.god.erb @@ -0,0 +1,19 @@ +God.watch do |w| + w.name = "apache2" + w.interval = 30.seconds # default + w.start = "<%= @params[:start] %>" + w.stop = "<%= @params[:stop] %>" + w.restart = "<%= @params[:restart] %>" + w.start_grace = 10.seconds + w.restart_grace = 10.seconds + w.pid_file = "/var/run/apache2.pid" + w.behavior(:clean_pid_file) + + w.start_if do |start| + start.condition(:process_running) do |c| + c.interval = 5.seconds + c.running = false + c.notify = 'admin' + end + end +end diff --git a/chef/cookbooks/apache2/templates/default/charset.erb b/chef/cookbooks/apache2/templates/default/charset.erb new file mode 100644 index 0000000..40d7198 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/charset.erb @@ -0,0 +1,6 @@ +# Read the documentation before enabling AddDefaultCharset. +# In general, it is only a good idea if you know that all your files +# have this encoding. It will override any encoding given in the files +# in meta http-equiv or xml encoding tags. + +#AddDefaultCharset UTF-8 diff --git a/chef/cookbooks/apache2/templates/default/default-site.erb b/chef/cookbooks/apache2/templates/default/default-site.erb new file mode 100644 index 0000000..a65ab53 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/default-site.erb @@ -0,0 +1,57 @@ + + ServerAdmin <%= node['apache']['contact'] %> + + DocumentRoot <%= node['apache']['docroot_dir'] %>/ + + Options FollowSymLinks + AllowOverride None + + /> + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + # This directive allows us to have apache2's default start page + # in /apache2-default/, but still have / go to the right place + #RedirectMatch ^/$ /apache2-default/ + + + ScriptAlias /cgi-bin/ <%= node['apache']['cgibin_dir'] %>/ + "> + AllowOverride None + Options ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + + ErrorLog <%= node['apache']['log_dir'] %>/<%= node['apache']['error_log'] %> + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog <%= node['apache']['log_dir'] %>/<%= node['apache']['access_log'] %> combined + ServerSignature On + + Alias /doc/ "/usr/share/doc/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + <% if %w{ rhel fedora }.include?(node['platform_family']) -%> + # + # This configuration file enables the default "Welcome" + # page if there is no default index page present for + # the root URL. To disable the Welcome page, comment + # out all the lines below. + # + + Options -Indexes + ErrorDocument 403 /error/noindex.html + + <% end -%> + diff --git a/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb b/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb new file mode 100644 index 0000000..dd1c2a7 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/etc-sysconfig-httpd.erb @@ -0,0 +1,31 @@ +# This file managed by Chef. Changes will be overwritten. + +# +# The default processing model (MPM) is the process-based +# 'prefork' model. A thread-based model, 'worker', is also +# available, but does not work with some modules (such as PHP). +# The service must be stopped before changing this variable. +# +HTTPD=<%= node['apache']['binary'] %> + +# +# To pass additional options (for instance, -D definitions) to the +# httpd binary at startup, set OPTIONS here. +# +#OPTIONS= + +# +# By default, the httpd process is started in the C locale; to +# change the locale in which the server runs, the HTTPD_LANG +# variable can be set. +# +#HTTPD_LANG=C + +# +# By default, the httpd process will create the file +# /var/run/httpd/httpd.pid in which it records its process +# identification number when it starts. If an alternate location is +# specified in httpd.conf (via the PidFile directive), the new +# location needs to be reported in the PIDFILE. +# +#PIDFILE=<%= node['apache']['pid_file'] %> diff --git a/chef/cookbooks/apache2/templates/default/mods/README b/chef/cookbooks/apache2/templates/default/mods/README new file mode 100644 index 0000000..df9f0bc --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/README @@ -0,0 +1,2 @@ +These configs are taken from a Debian apache2.2-common 2.2.11-3 install. They +work on CentOS 5.3 with a few conditions using erb. diff --git a/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb b/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb new file mode 100644 index 0000000..5ab139e --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb @@ -0,0 +1,24 @@ + +# +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname +# +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example, only "/icons/". If the fakename is slash-terminated, then the +# realname must also be slash terminated, and if the fakename omits the +# trailing slash, the realname must also omit it. +# +# We include the /icons/ alias for FancyIndexed directory listings. If +# you do not use FancyIndexing, you may comment this out. +# +Alias /icons/ "<%= node['apache']['icondir'] %>/" + +"> + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + + diff --git a/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb b/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb new file mode 100644 index 0000000..80efafe --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/auth_cas.conf.erb @@ -0,0 +1 @@ +CASCookiePath <%= node['apache']['cache_dir'] %>/mod_auth_cas/ \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/mods/auth_cas.load.erb b/chef/cookbooks/apache2/templates/default/mods/auth_cas.load.erb new file mode 100644 index 0000000..0e3a002 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/auth_cas.load.erb @@ -0,0 +1 @@ +LoadModule auth_cas_module <%= node['apache']['libexecdir'] %>/mod_auth_cas.so diff --git a/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb b/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb new file mode 100644 index 0000000..d226ecf --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb @@ -0,0 +1 @@ +LoadModule authopenid_module <%= node['apache']['libexecdir'] %>/mod_auth_openid.so diff --git a/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb b/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb new file mode 100644 index 0000000..3839093 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb @@ -0,0 +1,101 @@ + +# +# Directives controlling the display of server-generated directory listings. +# + +# +# IndexOptions: Controls the appearance of server-generated directory +# listings. +# Remove/replace the "Charset=UTF-8" if you don't use UTF-8 for your filenames. +# +IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +# It's a suffix rule, so simply matching "core" matches "score" as well ! +AddIcon /icons/bomb.gif /core +AddIcon (SND,/icons/sound2.gif) .ogg +AddIcon (VID,/icons/movie.gif) .ogm + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# Default icons for OpenDocument format +AddIcon /icons/odf6odt-20x22.png .odt +AddIcon /icons/odf6ods-20x22.png .ods +AddIcon /icons/odf6odp-20x22.png .odp +AddIcon /icons/odf6odg-20x22.png .odg +AddIcon /icons/odf6odc-20x22.png .odc +AddIcon /icons/odf6odf-20x22.png .odf +AddIcon /icons/odf6odb-20x22.png .odb +AddIcon /icons/odf6odi-20x22.png .odi +AddIcon /icons/odf6odm-20x22.png .odm + +AddIcon /icons/odf6ott-20x22.png .ott +AddIcon /icons/odf6ots-20x22.png .ots +AddIcon /icons/odf6otp-20x22.png .otp +AddIcon /icons/odf6otg-20x22.png .otg +AddIcon /icons/odf6otc-20x22.png .otc +AddIcon /icons/odf6otf-20x22.png .otf +AddIcon /icons/odf6oti-20x22.png .oti +AddIcon /icons/odf6oth-20x22.png .oth + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# RCS CVS *,v *,t + + diff --git a/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb b/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb new file mode 100644 index 0000000..2e41975 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb @@ -0,0 +1,16 @@ + + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE image/svg+xml + AddOutputFilterByType DEFLATE application/rss+xml + AddOutputFilterByType DEFLATE application/atom_xml + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/x-javascript + AddOutputFilterByType DEFLATE application/x-httpd-php + AddOutputFilterByType DEFLATE application/x-httpd-fastphp + AddOutputFilterByType DEFLATE application/x-httpd-eruby + diff --git a/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb b/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb new file mode 100644 index 0000000..e16fcb3 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb @@ -0,0 +1,5 @@ + + + DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm + + diff --git a/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb b/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb new file mode 100644 index 0000000..a252609 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/fastcgi.conf.erb @@ -0,0 +1,5 @@ + + AddHandler fastcgi-script .fcgi + #FastCgiWrapper /usr/lib/apache2/suexec + FastCgiIpcDir /var/lib/apache2/fastcgi + diff --git a/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb b/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb new file mode 100644 index 0000000..b314292 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb @@ -0,0 +1,10 @@ + + AddHandler fcgid-script .fcgi + IPCConnectTimeout 20 + + +<% if %w{ rhel fedora }.include?(node['platform_family']) -%> +# Sane place to put sockets and shared memory file +SocketPath run/mod_fcgid +SharememPath run/mod_fcgid/fcgid_shm +<% end -%> diff --git a/chef/cookbooks/apache2/templates/default/mods/include.conf.erb b/chef/cookbooks/apache2/templates/default/mods/include.conf.erb new file mode 100644 index 0000000..46f5547 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/include.conf.erb @@ -0,0 +1,4 @@ + + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/mods/include.erb b/chef/cookbooks/apache2/templates/default/mods/include.erb new file mode 100644 index 0000000..d5fbbed --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/include.erb @@ -0,0 +1,4 @@ + + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb b/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb new file mode 100644 index 0000000..3f21225 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb @@ -0,0 +1,198 @@ + + +# +# TypesConfig points to the file containing the list of mappings from +# filename extension to MIME-type. +# +<% case node['platform_family'] -%> +<% when "arch" -%> +TypesConfig <%= node['apache']['dir'] %>/conf/mime.types +<% when "freebsd" -%> +TypesConfig <%= node['apache']['dir'] %>/mime.types +<% else -%> +TypesConfig /etc/mime.types +<% end -%> + +# +# AddType allows you to add to or override the MIME configuration +# file mime.types for specific file types. +# +#AddType application/x-gzip .tgz +# +# AddEncoding allows you to have certain browsers uncompress +# information on the fly. Note: Not all browsers support this. +# Despite the name similarity, the following Add* directives have +# nothing to do with the FancyIndexing customization directives above. +# +#AddEncoding x-compress .Z +#AddEncoding x-gzip .gz .tgz +#AddEncoding x-bzip2 .bz2 +# +# If the AddEncoding directives above are commented-out, then you +# probably should define those extensions to indicate media types: +# +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz +AddType application/x-bzip2 .bz2 + +# +# DefaultLanguage and AddLanguage allows you to specify the language of +# a document. You can then use content negotiation to give a browser a +# file in a language the user can understand. +# +# Specify a default language. This means that all data +# going out without a specific language tag (see below) will +# be marked with this one. You probably do NOT want to set +# this unless you are sure it is correct for all cases. +# +# * It is generally better to not mark a page as +# * being a certain language than marking it with the wrong +# * language! +# +# DefaultLanguage nl +# +# Note 1: The suffix does not have to be the same as the language +# keyword --- those with documents in Polish (whose net-standard +# language code is pl) may wish to use "AddLanguage pl .po" to +# avoid the ambiguity with the common suffix for perl scripts. +# +# Note 2: The example entries below illustrate that in some cases +# the two character 'Language' abbreviation is not identical to +# the two character 'Country' code for its country, +# E.g. 'Danmark/dk' versus 'Danish/da'. +# +# Note 3: In the case of 'ltz' we violate the RFC by using a three char +# specifier. There is 'work in progress' to fix this and get +# the reference data for rfc1766 cleaned up. +# +# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) +# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) +# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) +# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) +# Norwegian (no) - Polish (pl) - Portugese (pt) +# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) +# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) +# +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +# See README.Debian for Spanish +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +# See README.Debian for Turkish +AddLanguage tr .tr +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +# +# Commonly used filename extensions to character sets. You probably +# want to avoid clashes with the language extensions, unless you +# are good at carefully testing your setup after each change. +# See http://www.iana.org/assignments/character-sets for the +# official list of charset names and their respective RFCs. +# +AddCharset us-ascii .ascii .us-ascii +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .arb .arabic +AddCharset ISO-8859-7 .iso8859-7 .grk .greek +AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew +AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk +AddCharset ISO-8859-10 .iso8859-10 .latin6 +AddCharset ISO-8859-13 .iso8859-13 +AddCharset ISO-8859-14 .iso8859-14 .latin8 +AddCharset ISO-8859-15 .iso8859-15 .latin9 +AddCharset ISO-8859-16 .iso8859-16 .latin10 +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5 .Big5 .big5 .b5 +AddCharset cn-Big5 .cn-big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8 .koi8 +AddCharset KOI8-E .koi8-e +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-U .koi8-u +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-7 .utf7 +AddCharset UTF-8 .utf8 +AddCharset UTF-16 .utf16 +AddCharset UTF-16BE .utf16be +AddCharset UTF-16LE .utf16le +AddCharset UTF-32 .utf32 +AddCharset UTF-32BE .utf32be +AddCharset UTF-32LE .utf32le +AddCharset euc-cn .euc-cn +AddCharset euc-gb .euc-gb +AddCharset euc-jp .euc-jp +AddCharset euc-kr .euc-kr +#Not sure how euc-tw got in - IANA doesn't list it??? +AddCharset EUC-TW .euc-tw +AddCharset gb2312 .gb2312 .gb +AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 +AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 +AddCharset shift_jis .shift_jis .sjis + +# +# AddHandler allows you to map certain file extensions to "handlers": +# actions unrelated to filetype. These can be either built into the server +# or added with the Action directive (see below) +# +# To use CGI scripts outside of ScriptAliased directories: +# (You will also need to add "ExecCGI" to the "Options" directive.) +# +#AddHandler cgi-script .cgi + +# +# For files that include their own HTTP headers: +# +#AddHandler send-as-is asis + +# +# For server-parsed imagemap files: +# +#AddHandler imap-file map + +# +# For type maps (negotiated resources): +# (This is enabled by default to allow the Apache "It Worked" page +# to be distributed in multiple languages.) +# +AddHandler type-map var + +# +# Filters allow you to process content before it is sent to the client. +# +# To parse .shtml files for server-side includes (SSI): +# (You will also need to add "Includes" to the "Options" directive.) +# +AddType text/html .shtml +AddOutputFilter INCLUDES .shtml + + diff --git a/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb b/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb new file mode 100644 index 0000000..0e3455b --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb @@ -0,0 +1,18 @@ + +# +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# +# Just list the languages in decreasing order of preference. We have +# more or less alphabetized them here. You probably want to change this. +# +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW + +# +# ForceLanguagePriority allows you to serve a result page rather than +# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) +# [in case no accepted languages matched the available variants] +# +ForceLanguagePriority Prefer Fallback + + diff --git a/chef/cookbooks/apache2/templates/default/mods/php5.conf.erb b/chef/cookbooks/apache2/templates/default/mods/php5.conf.erb new file mode 100644 index 0000000..5d2f911 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/php5.conf.erb @@ -0,0 +1,16 @@ + + + SetHandler application/x-httpd-php + + + SetHandler application/x-httpd-php-source + + # To re-enable php in user directories comment the following lines + # (from to .) Do NOT set it to On as it + # prevents .htaccess files from disabling it. + + + php_admin_value engine Off + + + diff --git a/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb b/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb new file mode 100644 index 0000000..46407a1 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb @@ -0,0 +1,19 @@ + + #turning ProxyRequests on and allowing proxying from all may allow + #spammers to use your proxy to send email. + + ProxyRequests Off + + + AddDefaultCharset off + Order deny,allow + Deny from all + #Allow from .example.com + + + # Enable/disable the handling of HTTP/1.1 "Via:" headers. + # ("Full" adds the server version; "Block" removes all outgoing Via: headers) + # Set to one of: Off | On | Full | Block + + ProxyVia On + diff --git a/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb b/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb new file mode 100644 index 0000000..6b7d6e2 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb @@ -0,0 +1,28 @@ + + +# +# The following directives modify normal HTTP response behavior to +# handle known problems with browser implementations. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. +# +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully +BrowserMatch "^gnome-vfs/1.0" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully + + diff --git a/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb b/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb new file mode 100644 index 0000000..2582f30 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb @@ -0,0 +1,77 @@ + +# +# Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the SSL library. +# The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. This means you then cannot use the /dev/random device +# because it would lead to very long connection times (as long as +# it requires to make more entropy available). But usually those +# platforms additionally provide a /dev/urandom device which doesn't +# block. So, if available, use this one instead. Read the mod_ssl User +# Manual for more details. +# +SSLRandomSeed startup builtin +SSLRandomSeed startup file:/dev/urandom 512 +SSLRandomSeed connect builtin +SSLRandomSeed connect file:/dev/urandom 512 + +## +## SSL Global Context +## +## All SSL configuration in this context applies both to +## the main server and all SSL-enabled virtual hosts. +## + +# +# Some MIME-types for downloading Certificates and CRLs +# +AddType application/x-x509-ca-cert .crt +AddType application/x-pkcs7-crl .crl + +# Pass Phrase Dialog: +# Configure the pass phrase gathering process. +# The filtering dialog program (`builtin' is a internal +# terminal dialog) has to provide the pass phrase on stdout. +SSLPassPhraseDialog builtin + +# Inter-Process Session Cache: +# Configure the SSL Session Cache: First the mechanism +# to use and second the expiring timeout (in seconds). +#SSLSessionCache dbm:/var/run/apache2/ssl_scache +<% if %w{ rhel fedora suse }.include?(node['platform_family']) -%> +SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) +<% elsif %w{ freebsd }.include?(node['platform_family']) -%> +SSLSessionCache shmcb:/var/run/ssl_scache(512000) +<% else -%> +SSLSessionCache shmcb:/var/run/apache2/ssl_scache +<% end -%> +SSLSessionCacheTimeout 300 + +# Semaphore: +# Configure the path to the mutual exclusion semaphore the +# SSL engine uses internally for inter-process synchronization. +<% if %w{ rhel fedora suse }.include?(node['platform_family']) -%> +SSLMutex default +<% elsif %w{ freebsd }.include?(node['platform_family']) -%> +SSLMutex file:/var/run/ssl_mutex +<% else -%> +SSLMutex file:/var/run/apache2/ssl_mutex +<% end -%> + +SSLHonorCipherOrder On +# SSL Cipher Suite: +# List the ciphers that the client is permitted to negotiate. +# See the mod_ssl documentation for a complete list. +# enable only secure ciphers: +SSLCipherSuite <%= node['apache']['mod_ssl']['cipher_suite'] %> +# Use this instead if you want to allow cipher upgrades via SGC facility. +# In this case you also have to use something like +# SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 +# see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html.en#upgradeenc +#SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + +# enable only secure protocols: SSLv3 and TLSv1, but not SSLv2 +SSLProtocol all -SSLv2 + + diff --git a/chef/cookbooks/apache2/templates/default/mods/status.conf.erb b/chef/cookbooks/apache2/templates/default/mods/status.conf.erb new file mode 100644 index 0000000..8bafc52 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/mods/status.conf.erb @@ -0,0 +1,26 @@ + +# +# Allow server status reports generated by mod_status, +# with the URL of http://servername/server-status +# Uncomment and change the ".example.com" to allow +# access from other hosts. +# + + SetHandler server-status + Order deny,allow + Deny from all + Allow from <%=node['apache']['status_allow_list']%> +# Allow from .example.com + +# +# ExtendedStatus controls whether Apache will generate "full" status +# information (ExtendedStatus On) or just basic information (ExtendedStatus +# Off) when the "server-status" handler is called. The default is Off. +# +<% if node['apache']['ext_status'] %> +ExtendedStatus On +<% else -%> +ExtendedStatus Off +<% end -%> +# + diff --git a/chef/cookbooks/apache2/templates/default/port_apache.erb b/chef/cookbooks/apache2/templates/default/port_apache.erb new file mode 100644 index 0000000..45aa56e --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/port_apache.erb @@ -0,0 +1,3 @@ +<% node['apache']['listen_ports'].each do |port| -%> +-A FWR -p tcp -m tcp --dport <%= port %> -j ACCEPT +<% end %> \ No newline at end of file diff --git a/chef/cookbooks/apache2/templates/default/ports.conf.erb b/chef/cookbooks/apache2/templates/default/ports.conf.erb new file mode 100644 index 0000000..cc3631e --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/ports.conf.erb @@ -0,0 +1,6 @@ +#This file generated via template by Chef. +<% @apache_listen_ports.each do |port| -%> +Listen <%= port %> +NameVirtualHost *:<%= port %> + +<% end -%> diff --git a/chef/cookbooks/apache2/templates/default/security.erb b/chef/cookbooks/apache2/templates/default/security.erb new file mode 100644 index 0000000..d40dbb4 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/security.erb @@ -0,0 +1,50 @@ +# +# Disable access to the entire file system except for the directories that +# are explicitly allowed later. +# +# This currently breaks the configurations that come with some web application +# Debian packages. It will be made the default for the release after lenny. +# +# +# AllowOverride None +# Order Deny,Allow +# Deny from all +# + + +# Changing the following options will not really affect the security of the +# server, but might make attacks slightly more difficult in some cases. + +# +# ServerTokens +# This directive configures what you return as the Server HTTP response +# Header. The default is 'Full' which sends information about the OS-Type +# and compiled in modules. +# Set to one of: Full | OS | Minimal | Minor | Major | Prod +# where Full conveys the most information, and Prod the least. +# +#ServerTokens Minimal +ServerTokens <%= node['apache']['servertokens'] %> + +# +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (internal error documents, FTP directory +# listings, mod_status and mod_info output etc., but not CGI generated +# documents or custom error documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +# +#ServerSignature Off +ServerSignature <%= node['apache']['serversignature'] %> + +# +# Allow TRACE method +# +# Set to "extended" to also reflect the request body (only for testing and +# diagnostic purposes). +# +# Set to one of: On | Off | extended +# +#TraceEnable Off +TraceEnable <%= node['apache']['traceenable'] %> + diff --git a/chef/cookbooks/apache2/templates/default/web_app.conf.erb b/chef/cookbooks/apache2/templates/default/web_app.conf.erb new file mode 100644 index 0000000..c5d9f95 --- /dev/null +++ b/chef/cookbooks/apache2/templates/default/web_app.conf.erb @@ -0,0 +1,47 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= a %> <% end %> + DocumentRoot <%= @params[:docroot] %> + RewriteEngine On + + > + Options <%= [@params[:directory_options] || "FollowSymLinks" ].flatten.join " " %> + AllowOverride <%= [@params[:allow_override] || "None" ].flatten.join " " %> + Order allow,deny + Allow from all + + + + Options FollowSymLinks + AllowOverride None + + + + SetHandler server-status + + Order Deny,Allow + Deny from all + Allow from 127.0.0.1 + + + LogLevel info + ErrorLog <%= node['apache']['log_dir'] %>/<%= @params[:name] %>-error.log + CustomLog <%= node['apache']['log_dir'] %>/<%= @params[:name] %>-access.log combined + + <% if @params[:directory_index] -%> + DirectoryIndex <%= [@params[:directory_index]].flatten.join " " %> + <% end -%> + + RewriteEngine On + RewriteLog <%= node['apache']['log_dir'] %>/<%= @application_name %>-rewrite.log + RewriteLogLevel 0 + + # Canonical host, <%= @params[:server_name] %> + RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301] + + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + diff --git a/chef/cookbooks/apache2/test/features/alias_paths.feature b/chef/cookbooks/apache2/test/features/alias_paths.feature new file mode 100644 index 0000000..796cba2 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/alias_paths.feature @@ -0,0 +1,12 @@ +Feature: Alias Paths + +In order to host a website with the URL structure different to the filesystem structure +As a developer +I want to be able to alias paths + + @default @mod_alias + Scenario: Aliased directory + Given a new webserver with aliasing enabled + And an alias defined + When I request the alias path + Then the aliased resource should be returned successfully diff --git a/chef/cookbooks/apache2/test/features/authenticate_basic.feature b/chef/cookbooks/apache2/test/features/authenticate_basic.feature new file mode 100644 index 0000000..f0c56a4 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authenticate_basic.feature @@ -0,0 +1,20 @@ +@mod_auth_basic +Feature: Basic Authentication + +In order to perform authorization or vary the provided content +As a developer +I want to authenticate the remote user + + Scenario: Authenticate access to a page + Given a new webserver configured to require authentication to access a page + When the user requests the secure page with no credentials + Then access will be rejected requiring authentication + + Scenario Outline: Authenticate access to a page (basic authentication) + Given a new webserver configured to require authentication to access a page + When the user requests the secure page authenticating with over basic auth + Then access will be + Examples: + | credentials | access | + | valid credentials | granted | + | invalid credentials | rejected requiring authentication | diff --git a/chef/cookbooks/apache2/test/features/authenticate_digest.feature b/chef/cookbooks/apache2/test/features/authenticate_digest.feature new file mode 100644 index 0000000..1cc65ed --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authenticate_digest.feature @@ -0,0 +1,20 @@ +@mod_auth_digest +Feature: Digest Authentication + +In order to perform authorization or vary the provided content +As a developer +I want to authenticate the remote user + + Scenario: Authenticate access to a page + Given a new webserver configured to require authentication to access a page + When the user requests the secure page with no credentials + Then access will be rejected requiring authentication + + Scenario Outline: Authenticate access to a page (digest authentication) + Given a new webserver configured to require authentication to access a page + When the user requests the secure page authenticating with over digest auth + Then access will be + Examples: + | credentials | access | + | valid credentials | granted | + | invalid credentials | rejected requiring authentication | diff --git a/chef/cookbooks/apache2/test/features/authenticate_openid.feature b/chef/cookbooks/apache2/test/features/authenticate_openid.feature new file mode 100644 index 0000000..597f4cd --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authenticate_openid.feature @@ -0,0 +1,11 @@ +@mod_auth_openid +Feature: OpenID Authentication + +In order to perform authorization or vary the provided content +As a developer +I want to authenticate the remote user + + Scenario: Authenticate access to a page + Given a new webserver configured to require authentication to access a page + When the user requests the secure page with no credentials + Then access will be rejected requiring OpenID authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_groupfile.feature b/chef/cookbooks/apache2/test/features/authorize_groupfile.feature new file mode 100644 index 0000000..ef80446 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authorize_groupfile.feature @@ -0,0 +1,16 @@ +@mod_authz_groupfile +Feature: Authorize access to content by user + +In order to restrict part of my website +As a developer +I want to restrict access to specific users + + Scenario: Authorize based on group file + Given a new webserver configured to authorize users listed in a group file + When the authenticated user is listed in the file + Then access will be granted + + Scenario: Valid authentication but not a member of the group + Given a new webserver configured to authorize users listed in a group file + When the authenticated user is not listed in the file + Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_host.feature b/chef/cookbooks/apache2/test/features/authorize_host.feature new file mode 100644 index 0000000..f2c2742 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authorize_host.feature @@ -0,0 +1,17 @@ +Feature: Authorize access to content by host + +In order to restrict part of my website +As a developer +I want to restrict access to known remote hosts + + @mod_authz_listed_host + Scenario: Known remote address + Given a new webserver configured to authorize access based on the remote address + When the remote address is listed as authorized + Then access will be granted + + @mod_authz_unlisted_host + Scenario: Unlisted remote address + Given a new webserver configured to authorize access based on the remote address + When the remote address is not listed as authorized + Then access will be denied diff --git a/chef/cookbooks/apache2/test/features/authorize_ldap.feature b/chef/cookbooks/apache2/test/features/authorize_ldap.feature new file mode 100644 index 0000000..cc1eb55 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authorize_ldap.feature @@ -0,0 +1,16 @@ +@mod_authnz_ldap +Feature: Authorize access to content against corporate directory + +In order to restrict part of my website +As a developer +I want to restrict access to people in my corporate directory + + Scenario: Authorized user access + Given a new webserver configured to authorize against a corporate directory + When the authenticated user is listed in the directory as authorized + Then access will be granted + + Scenario: User not in directory + Given a new webserver configured to authorize against a corporate directory + When the authenticated user is not listed in the directory as authorized + Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/authorize_users.feature b/chef/cookbooks/apache2/test/features/authorize_users.feature new file mode 100644 index 0000000..1ca1394 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/authorize_users.feature @@ -0,0 +1,16 @@ +@mod_authz_user +Feature: Authorize access to content by user + +In order to restrict part of my website +As a developer +I want to restrict access to specific users + + Scenario: Authorize named users + Given a new webserver configured to authorize access to specific named users + When the authenticated user is listed as authorized + Then access will be granted + + Scenario: Authorize named users + Given a new webserver configured to authorize access to specific named users + When the authenticated user is not listed as authorized + Then access will be rejected requiring authentication diff --git a/chef/cookbooks/apache2/test/features/basic_web_app.feature b/chef/cookbooks/apache2/test/features/basic_web_app.feature new file mode 100644 index 0000000..4031c0c --- /dev/null +++ b/chef/cookbooks/apache2/test/features/basic_web_app.feature @@ -0,0 +1,11 @@ +@basic_web_app +Feature: Deploy basic webapp + +In order to run my application +As a developer +I want to deploy a basic web application + + Scenario: Deploy basic webapp + Given a new webserver + When I request the root path of the webapp + Then the webapp default page will be returned diff --git a/chef/cookbooks/apache2/test/features/basic_webserver.feature b/chef/cookbooks/apache2/test/features/basic_webserver.feature new file mode 100644 index 0000000..014ae3b --- /dev/null +++ b/chef/cookbooks/apache2/test/features/basic_webserver.feature @@ -0,0 +1,16 @@ +@default +Feature: Serve web pages + +In order to run my application +As a developer +I want to respond to website requests + + Scenario: Request homepage + Given a new webserver + When I request the root url + Then the default page should be returned + + Scenario: Missing page + Given a new webserver + When I request a URL known not to exist + Then page not found should be returned diff --git a/chef/cookbooks/apache2/test/features/compress_server_response.feature b/chef/cookbooks/apache2/test/features/compress_server_response.feature new file mode 100644 index 0000000..943e982 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/compress_server_response.feature @@ -0,0 +1,16 @@ +@default @mod_deflate +Feature: Compress server response + +In order to reduce the time taken to retrieve web pages +As a developer +I want to enable compression on server responses + + Scenario: Deflate compression + Given a new webserver with deflate compression enabled + When the browser requests a page specifying that it supports compression + Then the response will be sent compressed + + Scenario: Deflate compression (no client support) + Given a new webserver with deflate compression enabled + When the browser requests a page specifying that it does not support compression + Then the response will be sent uncompressed diff --git a/chef/cookbooks/apache2/test/features/control_caching.feature b/chef/cookbooks/apache2/test/features/control_caching.feature new file mode 100644 index 0000000..d32afe9 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/control_caching.feature @@ -0,0 +1,11 @@ +@mod_expires +Feature: Control caching + +In order to control caching of responses by intermediate servers +As a developer +I want to control the expiry times on served pages + + Scenario: Set expiry time + Given a new webserver with support for setting expiry times enabled + When I request a path which has a cache directive applied + Then the expiry time returned will match that configured diff --git a/chef/cookbooks/apache2/test/features/directory_listing.feature b/chef/cookbooks/apache2/test/features/directory_listing.feature new file mode 100644 index 0000000..72aa287 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/directory_listing.feature @@ -0,0 +1,19 @@ +@default @mod_autoindex +Feature: Directory listing + +In order to allow browsing of the webserver filesystem +As a developer +I want to enable directory listing + + Scenario: View directory listing + Given a new webserver with directory listing enabled + And a path configured to allow directory listing + When I request the directory listing path + Then the directory listing should be returned successfully + + Scenario: Re-order files listed + Given a new webserver with directory listing enabled + And a path configured to allow directory listing with fancy indexing + When I request the directory listing path + Then the directory listing should be returned successfully + And I will be able to sort the files by size diff --git a/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature b/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature new file mode 100644 index 0000000..bc518ee --- /dev/null +++ b/chef/cookbooks/apache2/test/features/host_cgi_scripts.feature @@ -0,0 +1,11 @@ +@mod_cgi +Feature: Host CGI scripts + +In order to host dynamic websites +As a developer +I want to be able to host CGI scripts + + Scenario: Host CGI scripts + Given a new webserver with CGI support enabled + When a request is made to a CGI script that generates a list of environment variables + Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_perl_applications.feature b/chef/cookbooks/apache2/test/features/host_perl_applications.feature new file mode 100644 index 0000000..b9816d4 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/host_perl_applications.feature @@ -0,0 +1,11 @@ +@mod_perl +Feature: Host Perl applications + +In order to host dynamic websites +As a developer +I want to be able to host Perl applications + + Scenario: Host Perl application + Given a new webserver with Perl support enabled + When a request is made to a Perl script that generates a list of environment variables + Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_php_applications.feature b/chef/cookbooks/apache2/test/features/host_php_applications.feature new file mode 100644 index 0000000..618b999 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/host_php_applications.feature @@ -0,0 +1,11 @@ +@mod_php5 +Feature: Host PHP applications + +In order to host dynamic websites +As a developer +I want to be able to host PHP websites + + Scenario: Host PHP website + Given a new webserver with PHP support enabled + When a request is made to a PHP script that generates a list of environment variables + Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_python_applications.feature b/chef/cookbooks/apache2/test/features/host_python_applications.feature new file mode 100644 index 0000000..ae6f0d7 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/host_python_applications.feature @@ -0,0 +1,11 @@ +@mod_python +Feature: Host Python applications + +In order to host dynamic websites +As a developer +I want to be able to host Python applications + + Scenario: Host Python website + Given a new webserver with Python support enabled + When a request is made to a Python script that generates a list of environment variables + Then the expected environment variables will be present diff --git a/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature b/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature new file mode 100644 index 0000000..21daa4c --- /dev/null +++ b/chef/cookbooks/apache2/test/features/host_source_control_repositories.feature @@ -0,0 +1,12 @@ +Feature: Host source control repositories + +In order to provide access to source control +As a developer +I want to host source control repositories + + @mod_dav_svn + Scenario: Commit changes + Given a new webserver with subversion support enabled + And a subversion repository + When a developer commits a change to the repository + Then the change will be visible when browsing the repository diff --git a/chef/cookbooks/apache2/test/features/proxy_java_applications.feature b/chef/cookbooks/apache2/test/features/proxy_java_applications.feature new file mode 100644 index 0000000..4e75649 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/proxy_java_applications.feature @@ -0,0 +1,12 @@ +@java +Feature: Proxy Java applications + +In order to host dynamic websites +As a developer +I want be able to proxy requests to a Java application + + @mod_proxy_ajp + Scenario: Proxy Java application server + Given a new webserver with support for proxying to Java application servers enabled + When a request is made to a Java application that generates a list of request parameters + Then the expected request parameters will be present diff --git a/chef/cookbooks/apache2/test/features/secure_requests.feature b/chef/cookbooks/apache2/test/features/secure_requests.feature new file mode 100644 index 0000000..8346b54 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/secure_requests.feature @@ -0,0 +1,11 @@ +@mod_ssl +Feature: Secure requests + +In order to prevent a malicious third party from eavesdropping or hijacking a user session +As a developer +I want to secure communication between the client and server + + Scenario: Request homepage + Given a new webserver + When I request the root url over HTTPS + Then the default page should be returned diff --git a/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb b/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb new file mode 100644 index 0000000..f576c9a --- /dev/null +++ b/chef/cookbooks/apache2/test/features/step_definitions/svn_steps.rb @@ -0,0 +1,19 @@ +Given 'a subversion repository' do + +end + +When 'a developer commits a change to the repository' do + svn_repository '/svn/' do + svn_commit_new_file 'README', 'Hello World' + end +end + +Then 'the change will be visible when browsing the repository' do + begin + http_request('/svn/README').must_include 'Hello World' + ensure + svn_repository '/svn/' do + svn_remove_file 'README' + end + end +end diff --git a/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb b/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb new file mode 100644 index 0000000..f743189 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/step_definitions/webserver_steps.rb @@ -0,0 +1,156 @@ +Given /^a new webserver.*$/ do + +end + +Given /^an alias defined|a path configured to allow directory listing.*$/ do + # /icons/ is defined by default +end + +When /^a request is made to a (CGI|Java|Perl|Python|PHP) (?:script|application) that generates a list of (?:environment variables|request parameters)$/ do |script_type| + http_request case script_type + when 'CGI' then '/cgi-bin/env' + when 'Python' then '/env/python.py' + else "/env/#{script_type.downcase}" + end +end + +When 'I request a path which has a cache directive applied' do + http_request '/cachetest/' +end + +When 'I request a URL known not to exist' do + http_request '/this-path-does-not-exist' +end + +When 'I request as a known browser that only supports HTTP/1.0' do + @response_version = http_response_version('JDK/1.0', '1.0') +end + +When /^I request the (?:alias|directory listing) path$/ do + http_request '/icons/' +end + +When 'I request the root path of the webapp' do + http_request '/basic_web_app/' +end + +When /^I request the root url( over HTTPS)?$/ do |secure| + if secure + https_request '/' + else + http_request '/' + end +end + +When 'I request the status page from a remote host' do + http_request '/server-status/' +end + +When /^the authenticated user is (not )?listed (?:in the directory )(?:in the file|as authorized)$/ do |not_listed| + http_request '/secure/', + :basic_auth => {:username => not_listed ? 'meatballs' : 'bork', + :password => 'secret'} +end + +When 'the browser requests a page specifying that it does not support compression' do + @response_was_compressed = compresses_response?(:client_no_support) +end + +When 'the browser requests a page specifying that it supports compression' do + @response_was_compressed = compresses_response?(:client_supports) +end + +When /^the remote address is (not )?listed as authorized$/ do |not_listed| + http_request '/secure/' +end + +When /^the user requests the secure page authenticating with (in)?valid credentials over (basic|digest) auth$/ do |invalid, auth_type| + http_request '/secure/', "#{auth_type}_auth".to_sym => {:username => 'bork', + :password => invalid ? 'squirrel' : 'secret'} +end + +When 'the user requests the secure page with no credentials' do + http_request '/secure/' +end + +Then /^access will be (denied|rejected requiring (?:OpenID )?authentication|granted)$/ do |access| + http_response.code.must_equal({ + 'denied' => 403, + 'rejected requiring authentication' => 401, + 'rejected requiring OpenID authentication' => 200, + 'granted' => 200 + }[access]) + if access == 'rejected requiring OpenID authentication' + http_response.body.must_include 'This site is protected and requires that you identify yourself with an OpenID url.' + end +end + +Then 'I will be able to sort the files by size' do + http_request '/icons/?C=S;O=A' + # icons differ on different distros + dir_listing_entries[1].must_equal 'small/' +end + +Then 'page not found should be returned' do + http_response.body.must_include 'Not Found' + http_response.code.must_equal 404 +end + +Then 'simple statistics will be shown' do + http_response.body.must_include 'Apache Status' + ['Server uptime', 'requests currently being processed', 'idle workers'].each do |stat| + http_response.body.must_include stat + end +end + +Then 'the aliased resource should be returned successfully' do + http_response.body.must_include 'Index of /icons' + http_response.code.must_equal 200 +end + +Then 'the default page should be returned' do + assert default_page_present?(http_response.body) +end + +Then 'the directory listing should be returned successfully' do + http_response.body.must_include 'Index of /icons' + http_response.body.must_include 'Parent Directory' + dir_listing_entries.must_include 'README' + dir_listing_entries.must_include 'a.png' + http_response.code.must_equal 200 +end + +Then 'the expected environment variables will be present' do + env = environment_variables(http_response.body) + env['GATEWAY_INTERFACE'].must_include 'CGI/1.1' + env['SERVER_SOFTWARE'].must_equal 'Apache' +end + +Then 'the expected request parameters will be present' do + params = request_parameters(http_response.body) + params['Method'].must_equal 'GET' + params['Protocol'].must_equal 'HTTP/1.1' + params['Request URI'].must_equal '/examples/servlets/servlet/RequestInfoExample' +end + +Then 'the expiry time returned will match that configured' do + http_response.code.must_equal 200 + cache_time_seconds(http_response.headers).must_equal 60 + max_age_seconds(http_response.headers).must_equal 60 +end + +Then 'the response should be HTTP/1.0 also' do + @response_version.must_equal '1.0' +end + +Then /^the response will be sent (un)?compressed$/ do |expect_uncompressed| + if expect_uncompressed + refute @response_was_compressed + else + assert @response_was_compressed + end +end + +Then 'the webapp default page will be returned' do + http_response.body.must_include 'Hello World' +end diff --git a/chef/cookbooks/apache2/test/features/support/env.rb b/chef/cookbooks/apache2/test/features/support/env.rb new file mode 100644 index 0000000..8fb0fc0 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/support/env.rb @@ -0,0 +1,3 @@ +require 'minitest/spec' +World(MiniTest::Assertions) +MiniTest::Spec.new(nil) diff --git a/chef/cookbooks/apache2/test/features/support/svn_helpers.rb b/chef/cookbooks/apache2/test/features/support/svn_helpers.rb new file mode 100644 index 0000000..d9b3855 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/support/svn_helpers.rb @@ -0,0 +1,24 @@ +require 'tmpdir' + +def run(cmd) + %x{#{cmd}} + assert $?.success? +end + +def svn_commit_new_file(filename, content) + File.open(filename, 'w') {|f| f.write(content) } + run "svn add #{filename} && svn commit -m 'Committed a change.'" +end + +def svn_remove_file(filename) + run "svn rm #{filename} && svn commit -m 'Revert previous commit.'" +end + +def svn_repository(path) + Dir.mktmpdir do |dir| + Dir.chdir dir + run "svn co http://#{test_host}#{path}" + Dir.chdir File.join(dir, path) + yield + end +end diff --git a/chef/cookbooks/apache2/test/features/support/web_helpers.rb b/chef/cookbooks/apache2/test/features/support/web_helpers.rb new file mode 100644 index 0000000..4d49322 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/support/web_helpers.rb @@ -0,0 +1,86 @@ +require 'httparty' +require 'nokogiri' + +def test_host + ENV['TEST_HOST'] || 'localhost' +end + +def http_port + ENV['TEST_HTTP_PORT'] || 80 +end + +def https_port + ENV['TEST_HTTPS_PORT'] || 443 +end + +def cache_time_seconds(http_headers) + expiry_time = Time.parse(http_headers['expires']) + server_time = Time.parse(http_headers['date']) + expiry_time - server_time +end + +def compresses_response?(request_type) + # httparty rewrites the response to hide compression from us + encoding = %x{curl -s -i #{'--compressed ' if request_type == :client_supports} 'http://#{test_host}/' | grep 'Content-Encoding' | awk -F' ' '{print $2}'}.strip + %w{deflate gzip}.include?(encoding) +end + +def default_page_present?(body) + ['This is the default web page for this server.', + 'Apache HTTP Server Test Page'].any?{|msg| body.include? msg} +end + +# Filenames in a directory listing response +def dir_listing_entries + Nokogiri::HTML(http_response.body).xpath("//td/a/text()").map{|a| a.to_s} +end + +def environment_variables(response_body) + Hash[response_body.split("\n").map{|v| v.split('=')}] +end + +def http_request(path, options={}) + if options.key?(:digest_auth) + # HTTParty digest doesn't appear to work + @response = http_request_digest_curl(path, options) + else + @response = HTTParty.get("http://#{test_host}:#{http_port}#{path}", options) + end + @response +end + +def http_request_digest_curl(path, options) + credentials = "#{options[:digest_auth][:username]}:#{options[:digest_auth][:password]}" + curl_response = %x{curl -s -i --digest -u #{credentials} http://#{test_host}:#{http_port}#{path}} + assert $?.success? + @response = Class.new do + def initialize(response) + @curl_response = response + end + def code + @curl_response.scan(%r{HTTP/1.1 ([0-9]+)}).flatten.last.to_i + end + end.new(curl_response) +end + +def https_request(path) + @response = HTTParty.get("https://#{test_host}:#{https_port}#{path}") +end + +def http_response + @response +end + +def http_response_version(user_agent, protocol_version) + response_line = %x{curl -s #{'-0 ' if protocol_version == '1.0'} -i -A '#{user_agent}' 'http://#{test_host}/' | head -n1} + assert $?.success? + response_line.scan(/HTTP\/([0-9]+\.[0-9]+) [0-9]+.*/).flatten.first +end + +def max_age_seconds(http_headers) + http_headers['cache-control'].scan(/^max-age=([0-9]+)$/).flatten.first.to_i +end + +def request_parameters(response_body) + Hash[*Nokogiri::HTML(response_body).xpath("//td/text()").map{|h| h.to_s.strip.sub(/:$/, '')}] +end diff --git a/chef/cookbooks/apache2/test/features/support_older_browsers.feature b/chef/cookbooks/apache2/test/features/support_older_browsers.feature new file mode 100644 index 0000000..5158f66 --- /dev/null +++ b/chef/cookbooks/apache2/test/features/support_older_browsers.feature @@ -0,0 +1,11 @@ +@default @mod_setenvif +Feature: Support older browsers + + In order to be a good netizen + As a developer + I want to ensure that my server will respond to requests from older browsers + + Scenario: Support HTTP/1.0 + Given a new webserver + When I request as a known browser that only supports HTTP/1.0 + Then the response should be HTTP/1.0 also diff --git a/chef/cookbooks/apache2/test/kitchen/Kitchenfile b/chef/cookbooks/apache2/test/kitchen/Kitchenfile new file mode 100644 index 0000000..a0ed0ff --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/Kitchenfile @@ -0,0 +1,50 @@ +# +# Author:: Andrew Crump +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +cookbook "apache2" do + configuration "default" + # basic_web_app tests are in apache2_test cookbook + configuration "basic_web_app" + # module_recipes tests are in apache2_test cookbook + configuration "modules" + configuration "mod_auth_basic" + configuration "mod_auth_digest" + configuration "mod_auth_openid" + configuration "mod_auth_cas" + configuration "mod_authnz_ldap" + configuration "mod_authz_groupfile" + configuration "mod_authz_listed_host" + configuration "mod_authz_unlisted_host" + configuration "mod_authz_user" + configuration "mod_cgi" + configuration "mod_dav_svn" + configuration "mod_expires" + configuration "mod_fastcgi" + configuration "mod_include" + configuration "mod_perl" + configuration "mod_apreq2" + configuration "mod_php5" + configuration "mod_proxy_ajp" + configuration "mod_python" + configuration "mod_ssl" + configuration "mod_status_remote" + # placeholder until COOK-744 is fixed + #configuration "god_monitor" + exclude :platform => 'centos', :configuration => 'mod_authnz_ldap' + exclude :platform => 'centos', :configuration => 'mod_auth_cas' + run_list_extras ['apache2_test::setup'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile b/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile new file mode 100644 index 0000000..ff6ca50 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/Cheffile @@ -0,0 +1,4 @@ +cookbook 'openldap', + :git => 'https://github.com/opscode-cookbooks/openldap',:ref => 'foodcritic' + +cookbook 'god' diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md new file mode 100644 index 0000000..f6f3a39 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/README.md @@ -0,0 +1,82 @@ +Description +=========== + +This cookbook defines acceptance tests for Apache2. It includes: + +* A `features` sub-directory where the Cucumber features for the webserver + are defined. +* Recipes that configure individual modules for use in order to be tested. + +Requirements +============ + +## Cookbooks: + +This cookbook depends on the `apache2` cookbook. It also relies on the `yum` +cookbook in order to add the EPEL repository on RHEL-derived distributions. + +## Platforms: + +* Ubuntu +* CentOS + +Attributes +========== + +* `node['apache_test']['auth_username']` - The username of the user for testing + authentication and authorization. +* `node['apache_test']['auth_password']` - The password of the user for testing + authentication and authorization. +* `node['apache_test']['cache_expiry_seconds']` - The cache expiry time in + seconds. +* `node['apache_test']['app_dir']` - The local directory where test applications + will be deployed. +* `node['apache_test']['cgi_dir']` - The local directory where CGI applications + will be deployed. +* `node['apache_test']['root_dir']` - The root directory of the webserver. +* `node['apache_test']['remote_host_ip']` - The remote host IP address for + authorization. +* `node['apache_test']['ssl_dir']` - The local directory containing the generated SSL key and certificate. +* `node['apache_test']['ssl_cert_file']` - The SSL certificate file. +* `node['apache_test']['ssl_cert_key_file']` - The private key. + +Recipes +======= + +* `default` - Simply includes apache2::default for a vanilla apache install. +* `mod_auth_basic` - Adds a web_app behind basic authentication for testing. +* `mod_auth_digest` - Adds a web_app behind digest authenticaiton for testing. +* `mod_auth_openid` - Adds a web_app behind openid authentication for testing. +* `mod_authnz_ldap` - Adds a web_app behind ldap-based authorization for testing. +* `mod_authz_groupfile` - Adds a web_app behind groupfile-based authorization for testing. +* `mod_authz_listed_host` - Adds a web_app behind host-based authorization for testing. +* `mod_authz_unlisted_host` - Adds a web_app behind host-based authorization for testing. +* `mod_authz_user` - Adds a web_app behind username-based authorization for testing. +* `mod_cgi` - Adds a CGI script (bash) that prints environment variables for testing. +* `mod_dav_svn` - Adds a web_app with an empty Subversion repository for testing. +* `mod_expires` - Adds a web_app that sets caching expiry headers for testing. +* `mod_perl` - Adds a Perl script running under mod_perl that prints environment variables for testing. +* `mod_php5` - Adds a PHP script running under mod_php5 that prints environment variables for testing. +* `mod_proxy_ajp` - Installs Tomcat with examples and configures proxying over AJP. +* `mod_python` - Adds a Python script running under mod_python that prints environment variables for testing. +* `mod_ssl` - Adds a self-signed SSL certificate and default website for testing. +* `mod_status_remote` - Enables remote access to stats for testing. + +License and Authors +=================== + +Author:: Andrew Crump + + Copyright:: 2012, Opscode, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb new file mode 100644 index 0000000..323b63e --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/attributes/default.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: apache2_test +# Attributes:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['apache_test']['auth_username'] = 'bork' +default['apache_test']['auth_password'] = 'secret' +default['apache_test']['cache_expiry_seconds'] = 60 +default['apache_test']['app_dir'] = '/home/apache2/env' +default['apache_test']['cgi_dir'] = '/usr/lib/cgi-bin' +default['apache_test']['root_dir'] = '/var/www' +default['apache_test']['remote_host_ip'] = '127.0.0.1' +default['apache_test']['ssl_dir'] = '/home/apache2' +default['apache_test']['ssl_cert_file'] = "#{node['apache_test']['ssl_dir']}/server.crt" +default['apache_test']['ssl_cert_key_file'] = "#{node['apache_test']['ssl_dir']}/server.key" +default['apache_test']['svn_dir'] = '/home/apache2/svn' +default['domain'] = 'example.com' +default['openldap']['rootpw'] = '{SSHA}6BjlvtSbVCL88li8IorkqMSofkLio58/' +default['openldap']['rootpw_plain'] = 'secretsauce' +default['openldap']['slapd_rid'] = '000' +default['openldap']['auth_bindpw'] = 'yoltUnVik3' diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem new file mode 100644 index 0000000..7da82a1 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/ssl/ldap.example.com.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIDrjCCApYCCQCMPzF4wnKEjjANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC +VVMxEDAOBgNVBAgTB1NldmVyYWwxETAPBgNVBAcTCExvY2FsaXR5MRQwEgYDVQQK +EwtFeGFtcGxlIENvbTETMBEGA1UECxMKT3BlcmF0aW9uczEZMBcGA1UEAxMQbGRh +cC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPb3BzQGV4YW1wbGUuY29tMB4X +DTEyMTAwODIwNTgxOFoXDTIyMTAwNjIwNTgxOFowgZgxCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdTZXZlcmFsMREwDwYDVQQHEwhMb2NhbGl0eTEUMBIGA1UEChMLRXhh +bXBsZSBDb20xEzARBgNVBAsTCk9wZXJhdGlvbnMxGTAXBgNVBAMTEGxkYXAuZXhh +bXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD29wc0BleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMduncR64JPmOzDvtSU8U88+JX8StlZG +Ovd06tZ4x58jmIaqFXDLKmwU/+9f9mwSeT7jokX9Y/cLWP0HcAGs1KmGnodryYCW +DQ6+BBeRbBOw1RoaqgP4r3/ooXiTTUuVXxYcyKkx3Pn300iDHNo53/e9hxZB9DDv +KfFHL5A9kHA/bUpI8FBxa/SEGiV5gIkr15b3Jk7Zzydpqlz/6dlqaQxQBGqcm6yY +Ey/OqOuP+i7fKQfb9taPEAbnZNKbV4KajLlt/bl+60VHsdSBKZPROMt1bJZpdzcF +1SlIjFvFPp/G39Vkc8hvBXsUO8qivSiNirOhFMqxkS/f90N/8nD9Mu0CAwEAATAN +BgkqhkiG9w0BAQUFAAOCAQEArwmwsnhgAug1/ZuJAiT9VOR2yWhhU98IozoYLcE7 +45aRsv0G9qxd/zt4uN+xkgUP1xilDkuzVDuU2jdKgKTJAaDNy4mm7xtMoLzPNtEq +W+12EqxKzKKyGJz1B9iH7UsnM1ZCm6rTWe+Ij/hPU+A/qqOsOvLyi+Z93xauLW6E +tcLVlrsuoJ8k/P+u2s5Isxz+NOSeryg5WcNVGzkCjyXkxgIF05zl0bqN9RL0WukU +hWmJ3pAE15bz88tI2e1Z+5RPPo3cxD4Bw8+jH0HPKW8Cz51OUyr6stva3mgS0UXF +KhyTpqhhau4GE9cKpK+2n1iF6exdq9TAkdAhViW3uj7mJg== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAx26dxHrgk+Y7MO+1JTxTzz4lfxK2VkY693Tq1njHnyOYhqoV +cMsqbBT/71/2bBJ5PuOiRf1j9wtY/QdwAazUqYaeh2vJgJYNDr4EF5FsE7DVGhqq +A/ivf+iheJNNS5VfFhzIqTHc+ffTSIMc2jnf972HFkH0MO8p8UcvkD2QcD9tSkjw +UHFr9IQaJXmAiSvXlvcmTtnPJ2mqXP/p2WppDFAEapybrJgTL86o64/6Lt8pB9v2 +1o8QBudk0ptXgpqMuW39uX7rRUex1IEpk9E4y3Vslml3NwXVKUiMW8U+n8bf1WRz +yG8FexQ7yqK9KI2Ks6EUyrGRL9/3Q3/ycP0y7QIDAQABAoIBAQCdbQci8vBWL3Zo +AcCXfRCXVnJY5deDMPsZHXTHCh5h08JyAs7b87QwDz+coL6vvsFw0FXnM8d2WsHL +MtTKrbhNfdOkCITOeSQjkAFA7W1H+d2uNoAglG1M5cCYooZwdJ/Tn7MSRCcwNt3U +rpLW/Lp8IGMDQdrVyeTs7glwrfhXcYRpUFtT5AoweXFYIgGmZXD0LxmSdiY37V2X +zQ8vIjDn0wcNJYeqmOswP7iPS21HhAZGC4vfIji9DquQ4q6Pf8wRkW3KF7/BrmUl +9UXKb//Ja4xV50MSQoDQxmtjyrjnEfqYse2zLAn0QjVgqKgAzkP8+WpVrAmol6rG +KLd+VRtNAoGBAPIuvUIvRrjFVBWaAzv5pkE/DfpDA+6QSifSAJbqQOwSw3qAOBwq +RIQ//iBcHmxYyX0jhMVpP9wKZnVjDSr/9iLd4O7gvimxnYZp9vuvf2KZNbHaeky5 +niYIF65ObBRtAcAPETCEPhkYT0hFdyxJSRSBK73cQ9kzZAyROZYPvEivAoGBANLP +eUTGa25BG5xwzYF4NECSum9QK6QelH1TkO7nkv1c+Wp6HQ7G6hor5khxVkj5MpYk +nMdyZV4zU25OptM0QGMPwUwhj/MytpdUvBI2I5PAOq7lJbBCewW8lkagn+Pd1HD7 +IMBpvxe+M6LFftDiKnko+Z2zABkkRzVqGAPbL60jAoGAIKMr3j+AyGXPbxnSVcLP +JPvaZl+hqATJ+ZPTgIMRPL9KmLeu2BzaHviAxtujPfa5MKQYwIHumTjNlgRDQgg8 +o3ZDWe3vsq69C+A76K89+4uqMM3ArZZWOcndZyAqcJZAJiHhrygjNj6QcKzr4ov9 +zUWbH3sPqbXDRe5MVGzKcwECgYBxhpN66xPdqyhQZhr1lyMkhx/pZBYsat9yYndR +gNoSoWgb5CkT1SEq+OsppgdegvywCIV5juxx/1f1tlt2r9PgxRJGimh4Ap57/oDN +meQs5D92Aib6tcKEg1u2KzgPwV1vfn3TwN7MzXwHMy4pFTLkTqGmQEhUQcorRLgs +E3SoWwKBgEFEHZkpxcrjhs+S3Vr56ZSyYaMsXTbBx5Yz2e0/+ZASyXT9joFQ3au4 +IZoJCAhdSnRpj57FOuBHLwAJDSrUw/QxwD5VGI1i1MzzyejDW0590BYtNysXXdFF +r/Fp9Hh7Ms+cjzR7Fv8ccLQZPuie/nuNWT78gm8TkjNfwiMGgfrf +-----END RSA PRIVATE KEY----- diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb new file mode 100644 index 0000000..5d14e78 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/modules_test.rb @@ -0,0 +1,34 @@ +require File.expand_path('../support/helpers', __FILE__) + +# Test all the modules that are not specifically tested through a +# Kitchenfile configuration +# +# Does not test the modules in the default_modules attribute (those +# are tested in default_test) +%w{ + auth_digest + authnz_ldap + dav_fs + deflate + expires + fcgid + headers + ldap + proxy + proxy_balancer + proxy_connect + proxy_http + rewrite + wsgi + xsendfile +}.each do |expected_module| + + describe "apache2::mod_#{expected_module}" do + include Helpers::Apache + + it "installs mod_#{expected_module}" do + apache_enabled_modules.must_include "#{expected_module}_module" + end + + end +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..5054866 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,50 @@ +module Helpers + module Apache + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + def apache_config_parses? + acp = shell_out("#{node['apache']['binary']} -t") + acp.exitstatus == 0 + end + + def apache_configured_ports + port_config = File.read("#{node['apache']['dir']}/ports.conf") + port_config.scan(/^Listen ([0-9]+)/).flatten.map{|p| p.to_i} + end + + def apache_enabled_modules + apache_modules = shell_out("#{node['apache']['binary']} -M") + apache_modules.send( + if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 6.0 + :stderr + else + :stdout + end + ).split.select! {|i| i =~ /_module$/} + end + + def apache_service + service(case node['platform'] + when "debian","ubuntu" then "apache2" + when "freebsd" then "apache22" + else "httpd" + end) + end + + def config + file(case node['platform'] + when "debian","ubuntu" then "#{node['apache']['dir']}/apache2.conf" + when "freebsd" then "#{node['apache']['dir']}/httpd.conf" + else "#{node['apache']['dir']}/conf/httpd.conf" + end) + end + + def ran_recipe?(recipe) + node.run_state[:seen_recipes].keys.include?(recipe) + end + end +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb new file mode 100644 index 0000000..db67142 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/metadata.rb @@ -0,0 +1,88 @@ +maintainer "Andrew Crump" +maintainer_email "andrew@kotirisoftware.com" +license "Apache 2.0" +description "Acceptance tests for apache2" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.1.0" + +depends "apache2" +depends "jpackage" +depends "openldap" +depends "tomcat" +depends "yum" + +recipe "apache2_test::default", "Test example for default recipe" +recipe "apache2_test::mod_auth_basic", "Test example for basic authentication" +recipe "apache2_test::mod_auth_digest", "Test example for digest authentication" +recipe "apache2_test::mod_auth_openid", "Test example for openid authentication" +recipe "apache2_test::mod_authnz_ldap", "Test example for LDAP authentication" +recipe "apache2_test::mod_authz_groupfile", "Test example for group file authorization" +recipe "apache2_test::mod_authz_listed_host", "Test example for host-based authorization" +recipe "apache2_test::mod_authz_unlisted_host", "Test example for hosted-based authorization" +recipe "apache2_test::mod_authz_user", "Test example for named user authorization" +recipe "apache2_test::mod_cgi", "Test example for hosting a CGI script" +recipe "apache2_test::mod_expires", "Test example for setting cache expiry headers" +recipe "apache2_test::mod_dav_svn", "Test example for Subversion repository hosting" +recipe "apache2_test::mod_perl", "Test example for hosting a Perl application" +recipe "apache2_test::mod_proxy_ajp", "Test example for proxying requests to a Java application" +recipe "apache2_test::mod_php5", "Test example for hosting a PHP application" +recipe "apache2_test::mod_python", "Test example for hosting a Python application" +recipe "apache2_test::mod_ssl", "Test example for SSL" +recipe "apache2_test::mod_status_remote", "Test example for viewing server status" + +%w{centos ubuntu}.each do |os| + supports os +end + +attribute "apache_test/auth_username", + :display_name => "Test Username", + :description => "Username for the test user", + :default => "bork" + +attribute "apache_test/auth_password", + :display_name => "Test Password", + :description => "Password for the test user", + :default => "secret" + +attribute "apache_test/cache_expiry_seconds", + :display_name => "Cache Expiry (Seconds)", + :description => "The expiry time to set in caching response headers", + :default => "60" + +attribute "apache_test/app_dir", + :display_name => "Application Directory", + :description => "Parent directory to deploy test applications under", + :default => "/home/apache2/env" + +attribute "apache_test/cgi_dir", + :display_name => "CGI Directory", + :description => "Directory to install CGI scripts into", + :default => "/usr/lib/cgi-bin" + +attribute "apache_test/root_dir", + :display_name => "Root Directory", + :description => "Webserver document root directory", + :default => "/var/www" + +attribute "apache_test/remote_host_ip", + :display_name => "Remote Host IP", + :description => "IP Address to allow requests from", + :default => "192.168" + +attribute "apache_test/ssl_dir", + :display_name => "SSL Directory", + :description => "Directory for SSL certificates", + :default => "/home/apache2" + +attribute "apache_test/ssl_cert_file", + :display_name => "SSL Certificate Path", + :description => "File path for the generated self-signed certificate" + +attribute "apache_test/ssl_cert_key_file", + :display_name => "SSL Certificate Private Key", + :description => "File path for the generated private key" + +attribute "apache_test/svn_dir", + :display_name => "Subversion Directory", + :description => "File path for test Subversion repository", + :default => "/home/apache2/svn" diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb new file mode 100644 index 0000000..468b8d8 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/basic_web_app.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: basic_web_app +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +app_dir = "#{node['apache_test']['root_dir']}/basic_web_app" + +directory app_dir do + action :create +end + +file "#{app_dir}/index.html" do + content "Hello World" + action :create +end + +web_app "basic_webapp" do + cookbook "apache2" + server_name node['hostname'] + server_aliases [node['fqdn']] + docroot app_dir +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb new file mode 100644 index 0000000..bb80e15 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb new file mode 100644 index 0000000..1488168 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/god_monitor.rb @@ -0,0 +1,19 @@ +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::god_monitor" diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb new file mode 100644 index 0000000..93022a7 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_basic.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_auth_basic +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_auth_basic" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +execute "add-credentials" do + command "htpasswd -b -c #{node['apache_test']['root_dir']}/secure/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']}" + action :run +end + +web_app "secure" do + template "auth_basic.conf.erb" + auth_user_file "#{node['apache_test']['root_dir']}/secure/.htpasswd" +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb new file mode 100644 index 0000000..cf13ee5 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_digest.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_auth_digest +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_auth_digest" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +# htdigest won't read the password from STDIN +bash "add_credentials" do + code %Q{ + (echo -n "#{node['apache_test']['auth_username']}:private area:" && echo -n "#{node['apache_test']['auth_username']}:private area:#{node['apache_test']['auth_password']}" | md5sum | awk '{print $1}') > /#{node['apache_test']['root_dir']}/secure/.htdigest + } +end + +web_app "secure" do + template "auth_digest.conf.erb" + auth_user_file "#{node['apache_test']['root_dir']}/secure/.htdigest" +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb new file mode 100644 index 0000000..9a2b352 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_auth_openid.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_auth_openid +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_auth_openid" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +web_app "secure" do + template "auth_openid.conf.erb" +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb new file mode 100644 index 0000000..e4359be --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authnz_ldap.rb @@ -0,0 +1,63 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_authnz_ldap +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +directory "/var/cache/local/preseeding" do + recursive true + action :create + only_if { platform?('debian', 'ubuntu') } +end + +include_recipe "openldap::server" + +service "slapd" do + action :start +end + +cbf = resources("cookbook_file[#{node['openldap']['ssl_dir']}/#{node['openldap']['server']}.pem]") +cbf.cookbook "apache2_test" + +ldif_path = "/tmp/entries.ldif" + +template ldif_path do + source "entries.ldif.erb" + action :create +end + +bash "load-directory-entries" do + code %Q{ + ldapsearch -x -D 'cn=admin,#{node['openldap']['basedn']}' -w '#{node['openldap']['rootpw_plain']}' -b '#{node['openldap']['basedn']}' + if [ $? -ne 0 ] + then + ldapadd -x -D 'cn=admin,#{node['openldap']['basedn']}' -w '#{node['openldap']['rootpw_plain']}' -f #{ldif_path} + fi + } + action :run +end + +include_recipe "apache2::default" +include_recipe "apache2::mod_ldap" +include_recipe "apache2::mod_authnz_ldap" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +web_app "secure" do + template "authnz_ldap.conf.erb" + base_dn node['openldap']['basedn'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb new file mode 100644 index 0000000..a18bc32 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_groupfile.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_authz_groupfile +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +secure_dir = "#{node['apache_test']['root_dir']}/secure" +group_name = 'swedishchef' + +directory secure_dir do + action :create +end + +bash "add-credentials" do + code %Q{ + htpasswd -b -c #{secure_dir}/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']} + htpasswd -b #{secure_dir}/.htpasswd meatballs secret + } + action :run +end + +file "#{secure_dir}/.htgroups" do + content "#{group_name}:#{node['apache_test']['auth_username']}" +end + +include_recipe "apache2::mod_authz_groupfile" +web_app "secure" do + template "authz_groupfile.conf.erb" + secure_dir secure_dir + group_name group_name +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb new file mode 100644 index 0000000..e9ac4f3 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_listed_host.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_authz_listed_host +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_authz_host" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +web_app "secure" do + template "authz_host.conf.erb" + remote_host_ip node['apache_test']['remote_host_ip'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb new file mode 100644 index 0000000..707a04d --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_unlisted_host.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_authz_unlisted_host +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_authz_host" + +directory "#{node['apache_test']['root_dir']}/secure" do + action :create +end + +web_app "secure" do + template "authz_host.conf.erb" + remote_host_ip '8.8.8.8' +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb new file mode 100644 index 0000000..aa9204f --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_authz_user.rb @@ -0,0 +1,41 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_authz_user +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "apache2::default" + +secure_dir = "#{node['apache_test']['root_dir']}/secure" + +directory secure_dir do + action :create +end + +bash "add-credentials" do + code %Q{ + htpasswd -b -c #{secure_dir}/.htpasswd #{node['apache_test']['auth_username']} #{node['apache_test']['auth_password']} + htpasswd -b #{secure_dir}/.htpasswd meatballs secret + } + action :run +end + +include_recipe "apache2::mod_authz_user" + +web_app "secure" do + template "authz_user.conf.erb" + secure_dir secure_dir + username node['apache_test']['auth_username'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb new file mode 100644 index 0000000..cb407e9 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_cgi.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_cgi +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_cgi" + +directory node['apache_test']['cgi_dir'] do + action :create +end + +file "#{node['apache_test']['cgi_dir']}/env" do + content %q{ +#!/bin/bash +echo -e "Content-type: text/plain\n" +/usr/bin/env +}.strip + mode "0755" + action :create +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb new file mode 100644 index 0000000..ae365cd --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_dav_svn.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_dav_svn +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +package "subversion" do + action :install +end + +include_recipe "apache2::mod_dav" +include_recipe "apache2::mod_dav_svn" + +directory node['apache_test']['svn_dir'] do + owner node['apache']['user'] + group node['apache']['group'] + recursive true + action :create +end + +execute "create-repo" do + user node['apache']['user'] + command "svnadmin create --config-dir #{Chef::Config[:file_cache_path]} #{node['apache_test']['svn_dir']}" + not_if "bash -c 'svnadmin verify #{node['apache_test']['svn_dir']}'" +end + +web_app "svn" do + template "svn_repo.conf.erb" + repo_dir node['apache_test']['svn_dir'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb new file mode 100644 index 0000000..bc6bfd7 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_expires.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_expires +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_expires" + +directory "#{node['apache_test']['root_dir']}/cachetest" do + action :create +end + +web_app "cachetest" do + template "cache_test.conf.erb" + cache_expiry_seconds node['apache_test']['cache_expiry_seconds'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb new file mode 100644 index 0000000..56aaec7 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_perl.rb @@ -0,0 +1,66 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_perl +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +include_recipe "yum::epel" if platform?("centos") + +include_recipe "apache2::mod_perl" + +package "perl-CGI-SpeedyCGI" do + action :install + only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } +end + +file "#{node['apache']['dir']}/conf.d/apreq.conf" do + action :delete + only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } +end + +file "#{node['apache']['dir']}/conf.d/perl.conf" do + action :delete + only_if { platform?("redhat", "centos", "scientific", "fedora", "amazon") } +end + +directory node['apache_test']['app_dir'] do + recursive true + action :create +end + +file "#{node['apache_test']['app_dir']}/perl" do + content %q{ +#!/usr/bin/perl -wT +use strict; +use CGI qw(:standard); +use CGI::Carp qw(warningsToBrowser fatalsToBrowser); + +print header('text/plain'); + +foreach my $key (sort(keys(%ENV))) { + print "$key=$ENV{$key}\n"; +} +}.strip + mode "0755" + action :create +end + +web_app "perl_env" do + template "perl_env.conf.erb" + app_dir node['apache_test']['app_dir'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb new file mode 100644 index 0000000..3378123 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_php5.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_php5 +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +package "which" do + action :install + only_if { platform_family?("rhel", "fedora") } +end + +include_recipe "apache2::mod_php5" + +directory node['apache_test']['app_dir'] do + recursive true + action :create +end + +file "#{node['apache_test']['app_dir']}/php" do + content %q{ + $key_value) { + print $key_name . "=" . $key_value . "\n"; +} +?> +}.strip + mode "0755" + action :create +end + +web_app "php_env" do + template "php_env.conf.erb" + app_dir node['apache_test']['app_dir'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb new file mode 100644 index 0000000..c006af2 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_proxy_ajp.rb @@ -0,0 +1,44 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_ajp +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_proxy" +include_recipe "apache2::mod_proxy_ajp" + +if platform_family?("rhel") && node['platform_version'].to_f < 6.0 + include_recipe "jpackage::default" +end + +include_recipe "tomcat::default" + +if platform?("debian","ubuntu") + package "tomcat6-examples" do + action :install + end +else + package "tomcat6-webapps" do + action :install + end +end + +web_app "java_env" do + template "java_env.conf.erb" + ajp_host 'localhost' + ajp_port 8009 +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb new file mode 100644 index 0000000..ea22473 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_python.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_python +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +yum_repository "epel" do + url 'http://dl.fedoraproject.org/pub/epel/$releasever/$basearch/' + only_if { platform_family?("rhel", "fedora") } +end + +include_recipe "apache2::mod_python" + +directory node['apache_test']['app_dir'] do + recursive true + action :create +end + +file "#{node['apache_test']['app_dir']}/python.py" do + content %q{ +#!/usr/bin/python +import sys +sys.stderr = sys.stdout +import os +from cgi import escape + +print "Content-type: text/plain" +print +for k in sorted(os.environ): + print "%s=%s" %(escape(k), escape(os.environ[k])) +}.strip + mode "0755" + action :create +end + +web_app "python_env" do + template "python_env.conf.erb" + app_dir node['apache_test']['app_dir'] +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb new file mode 100644 index 0000000..b5b0e84 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/mod_ssl.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: apache2_test +# Recipe:: mod_ssl +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" +include_recipe "apache2::mod_ssl" + +directory node['apache_test']['ssl_dir'] do + owner node['apache']['user'] + group node['apache']['group'] + recursive true + action :create +end + +execute "create-private-key" do + command "openssl genrsa > #{node['apache_test']['ssl_cert_key_file']}" + not_if "test -f #{node['apache_test']['ssl_cert_key_file']}" +end + +execute "create-certficate" do + command %Q{openssl req -new -x509 -key #{node['apache_test']['ssl_cert_key_file']} -out #{node['apache_test']['ssl_cert_file']} -days 1 < node['apache_test']['remote_host_ip']}) +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb new file mode 100644 index 0000000..74735e7 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/modules.rb @@ -0,0 +1,43 @@ +# +# Cookbook:: apache2_test +# Recipe:: modules +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2::default" + +# Duplicates the list in the modules_test minitest, which is +# distasteful duplication. +%w{ + auth_digest + authnz_ldap + dav_fs + deflate + expires + fcgid + headers + ldap + proxy + proxy_balancer + proxy_connect + proxy_http + rewrite + wsgi + xsendfile +}.each do |a2mod| + include_recipe "apache2::mod_#{a2mod}" +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb new file mode 100644 index 0000000..99c3ce7 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/recipes/setup.rb @@ -0,0 +1,18 @@ +case node['platform_family'] + when 'debian' + %w{libxml2 libxml2-dev libxslt1-dev}.each do |pkg| + package pkg do + action :install + end + end + when 'rhel' + %w{gcc make ruby-devel libxml2 libxml2-devel libxslt libxslt-devel}.each do |pkg| + package pkg do + action :install + end + end +end + +package "curl" do + action :install +end diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb new file mode 100644 index 0000000..b28fecd --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_basic.conf.erb @@ -0,0 +1,6 @@ + + AuthUserFile "<%= @params[:auth_user_file] %>" + AuthType basic + AuthName "private area" + Require valid-user + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb new file mode 100644 index 0000000..c0014ef --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_digest.conf.erb @@ -0,0 +1,7 @@ + + AuthUserFile "<%= @params[:auth_user_file] %>" + AuthType digest + AuthDigestDomain /secure/ + AuthName "private area" + Require valid-user + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb new file mode 100644 index 0000000..5e55f84 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/auth_openid.conf.erb @@ -0,0 +1,8 @@ + + + AuthType OpenID + AuthOpenIDTrustRoot http://opscode.example.com + AuthName "private area" + Require valid-user + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb new file mode 100644 index 0000000..958aecc --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authnz_ldap.conf.erb @@ -0,0 +1,9 @@ + + + AuthType basic + AuthBasicProvider "ldap" + AuthLDAPUrl "ldap://localhost:389/<%= @params[:base_dn] %>?uid?sub?objectClass=inetOrgPerson" + AuthName "private area" + Require valid-user + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb new file mode 100644 index 0000000..b57087f --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_groupfile.conf.erb @@ -0,0 +1,7 @@ + + AuthUserFile "<%= @params[:secure_dir] %>/.htpasswd" + AuthGroupFile "<%= @params[:secure_dir] %>/.htgroups" + AuthType basic + AuthName "private area" + Require group <%= @params[:group_name] %> + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb new file mode 100644 index 0000000..6ef29d4 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_host.conf.erb @@ -0,0 +1,4 @@ + + Deny from all + Allow from <%= @params[:remote_host_ip] %> + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb new file mode 100644 index 0000000..80b632c --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/authz_user.conf.erb @@ -0,0 +1,6 @@ + + AuthUserFile "<%= @params[:secure_dir] %>/.htpasswd" + AuthType basic + AuthName "private area" + Require user <%= @params[:username] %> + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb new file mode 100644 index 0000000..2c12972 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/cache_test.conf.erb @@ -0,0 +1,4 @@ + + ExpiresActive On + ExpiresDefault A<%= @params[:cache_expiry_seconds] %> + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb new file mode 100644 index 0000000..24d9c3b --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/entries.ldif.erb @@ -0,0 +1,18 @@ +dn: dc=example,dc=com +dc: example +objectClass: dcObject +objectClass: organization +o: Example, Inc. + +dn: ou=people,dc=example,dc=com +ou: people +objectclass: organizationalunit + +dn: cn=bork,ou=people,dc=example,dc=com +objectclass: inetOrgPerson +cn: bork +sn: bork +uid: bork +userpassword: secret +mail: bork@example.com +ou: Catering diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb new file mode 100644 index 0000000..428ffc6 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/java_env.conf.erb @@ -0,0 +1,6 @@ + + ProxyPass /env/java ajp://<%= @params[:ajp_host] %>:<%= @params[:ajp_port] %>/examples/servlets/servlet/RequestInfoExample + + Allow from all + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb new file mode 100644 index 0000000..c76a9e8 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/perl_env.conf.erb @@ -0,0 +1,9 @@ + + Alias /env/ <%= @params[:app_dir] %>/ + + + SetHandler perl-script + PerlResponseHandler ModPerl::Registry + Options ExecCGI + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb new file mode 100644 index 0000000..66a4e60 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/php_env.conf.erb @@ -0,0 +1,7 @@ + + Alias /env/ <%= @params[:app_dir] %>/ + + + SetHandler php5-script + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb new file mode 100644 index 0000000..9c101a4 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/python_env.conf.erb @@ -0,0 +1,8 @@ + + Alias /env/ <%= @params[:app_dir] %>/ + + AddHandler mod_python .py + PythonHandler mod_python.cgihandler + PythonDebug On + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb new file mode 100644 index 0000000..a8bc39f --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/ssl.conf.erb @@ -0,0 +1,13 @@ + + ServerName <%= @params[:server_name] %> + DocumentRoot <%= @params[:document_root] %> + + Options -Indexes + ErrorDocument 403 /error/noindex.html + + + SSLEngine on + SSLCertificateFile <%= @params[:ssl_cert_file] %> + SSLCertificateKeyFile <%= @params[:ssl_cert_key_file] %> + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb new file mode 100644 index 0000000..9ff8c78 --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/status.conf.erb @@ -0,0 +1,9 @@ + + + SetHandler server-status + Order deny,allow + Deny from all + Allow from localhost ip6-localhost + Allow from <%= @remote_host %> + + diff --git a/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb new file mode 100644 index 0000000..cf8154e --- /dev/null +++ b/chef/cookbooks/apache2/test/kitchen/cookbooks/apache2_test/templates/default/svn_repo.conf.erb @@ -0,0 +1,6 @@ + + +DAV svn +SVNPath <%= @params[:repo_dir] %> + + diff --git a/chef/cookbooks/apt/.kitchen.yml b/chef/cookbooks/apt/.kitchen.yml new file mode 100644 index 0000000..49c3a45 --- /dev/null +++ b/chef/cookbooks/apt/.kitchen.yml @@ -0,0 +1,68 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: debian-7.1.0 + driver_config: + box: opscode-debian-7.1.0 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-13.04 + driver_config: + box: opscode-ubuntu-13.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box + run_list: + - recipe[apt] + +suites: +- name: default + run_list: + - recipe[minitest-handler] + - recipe[apt_test] + attributes: {} + +- name: cacher-client + run_list: + - recipe[minitest-handler] + - recipe[apt_test::cacher-client] + attributes: {} + +- name: cacher-ng + run_list: + - recipe[minitest-handler] + - recipe[apt_test::cacher-ng] + attributes: {} + +- name: cacher-ng-client + run_list: + - recipe[minitest-handler] + - recipe[apt_test::cacher-ng-client] + attributes: + apt: + cacher_dir: '/tmp/apt-cacher' + cacher_port: '9876' + compiletime: true + +- name: lwrps + run_list: + - recipe[minitest-handler] + - recipe[apt_test::lwrps] + attributes: {} diff --git a/chef/cookbooks/apt/Berksfile b/chef/cookbooks/apt/Berksfile new file mode 100644 index 0000000..91b26e6 --- /dev/null +++ b/chef/cookbooks/apt/Berksfile @@ -0,0 +1,8 @@ +site :opscode + +metadata + +group :integration do + cookbook "minitest-handler" + cookbook "apt_test", :path => "./test/cookbooks/apt_test" +end diff --git a/chef/cookbooks/apt/CHANGELOG.md b/chef/cookbooks/apt/CHANGELOG.md new file mode 100644 index 0000000..fb2046c --- /dev/null +++ b/chef/cookbooks/apt/CHANGELOG.md @@ -0,0 +1,119 @@ +apt Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the apt cookbook. + + +v2.1.1 +------ +### Bug +- **[COOK-1856](https://tickets.opscode.com/browse/COOK-1856)** - Match GPG keys without case sensitivity + +v2.1.0 +------ +- [COOK-3426]: cacher-ng fails with restrict_environment set to true +- [COOK-2859]: cacher-client executes out of order +- [COOK-3052]: Long GPG keys are downloaded on every run +- [COOK-1856]: apt cookbook should match keys without case sensitivity +- [COOK-3255]: Attribute name incorrect in README +- [COOK-3225]: Call use_inline_resources only if defined +- [COOK-3386]: Cache dir for apt-cacher-ng +- [COOK-3291]: apt_repository: enable usage of a keyserver on port 80 +- Greatly expanded test coverage with ChefSpec and Test-Kitchen + +v2.0.0 +------ +### Bug + +- [COOK-2258]: apt: LWRP results in error under why-run mode in apt 1.9.0 cookbook + +v1.10.0 +------- +### Improvement + +- [COOK-2885]: Improvements for apt cache server search + +### Bug + +- [COOK-2441]: Apt recipe broken in new chef version +- [COOK-2660]: Create Debian 6.0 "squeeze" specific template for + apt-cacher-ng + +v1.9.2 +------ +- [COOK-2631] - Create Ubuntu 10.04 specific template for apt-cacher-ng + +v1.9.0 +------ +- [COOK-2185] - Proxy for apt-key +- [COOK-2338] - Support pinning by glob() or regexp + +v1.8.4 +------ +- [COOK-2171] - Update README to clarify required Chef version: 10.18.0 + or higher. + +v1.8.2 +------ +- [COOK-2112] - need [] around "arch" in sources.list entries +- [COOK-2171] - fixes a regression in the notification + +v1.8.0 +------ +- [COOK-2143] - Allow for a custom cacher-ng port +- [COOK-2171] - On `apt_repository.run_action(:add)` the source file + is not created. +- [COOK-2184] - apt::cacher-ng, use `cacher_port` attribute in + acng.conf + +v1.7.0 +------ +- [COOK-2082] - add "arch" parameter to apt_repository LWRP + +v1.6.0 +------ +- [COOK-1893] - `apt_preference` use "`package_name`" resource instead of "name" +- [COOK-1894] - change filename for sources.list.d files +- [COOK-1914] - Wrong dir permissions for /etc/apt/preferences.d/ +- [COOK-1942] - README.md has wrong name for the keyserver attribute +- [COOK-2019] - create 01proxy before any other apt-get updates get executed + +v1.5.2 +------ +- [COOK-1682] - use template instead of file resource in apt::cacher-client +- [COOK-1875] - cacher-client should be Environment-aware + +V1.5.0 +------ +- [COOK-1500] - Avoid triggering apt-get update +- [COOK-1548] - Add execute commands for autoclean and autoremove +- [COOK-1591] - Setting up the apt proxy should leave https + connections direct +- [COOK-1596] - execute[apt-get-update-periodic] never runs +- [COOK-1762] - create /etc/apt/preferences.d directory +- [COOK-1776] - apt key check isn't idempotent + +v1.4.8 +------ +* Adds test-kitchen support +- [COOK-1435] - repository lwrp is not idempotent with http key + +v1.4.6 +------ +- [COOK-1530] - apt_repository isn't aware of update-success-stamp + file (also reverts COOK-1382 patch). + +v1.4.4 +------ +- [COOK-1229] - Allow cacher IP to be set manually in non-Chef Solo + environments +- [COOK-1530] - Immediately update apt-cache when sources.list file is dropped off + +v1.4.2 +------ +- [COOK-1155] - LWRP for apt pinning + +v1.4.0 +------ +- [COOK-889] - overwrite existing repo source files +- [COOK-921] - optionally use cookbook\_file or remote\_file for key +- [COOK-1032] - fixes problem with apt repository key installation diff --git a/chef/cookbooks/apt/CONTRIBUTING b/chef/cookbooks/apt/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/apt/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/apt/LICENSE b/chef/cookbooks/apt/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/apt/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/apt/README.md b/chef/cookbooks/apt/README.md new file mode 100644 index 0000000..32831bd --- /dev/null +++ b/chef/cookbooks/apt/README.md @@ -0,0 +1,239 @@ +Description +=========== + +This cookbook includes recipes to execute apt-get update to ensure the +local APT package cache is up to date. There are recipes for managing +the apt-cacher-ng caching proxy and proxy clients. It also includes a +LWRP for managing APT repositories in /etc/apt/sources.list.d as well as +an LWRP for pinning packages via /etc/apt/preferences.d. + +Requirements +============ + +Version 2.0.0+ of this cookbook requires **Chef 11.0.0** or later. + +If your Chef version is earlier than 11.0.0, use version 1.10.0 of +this cookbook. + +See [COOK-2258](http://tickets.opscode.com/browse/COOK-2258) for more +information on this requirement. + +Version 1.8.2 to 1.10.0 of this cookbook requires **Chef 10.16.4** or +later. + +If your Chef version is earlier than 10.16.4, use version 1.7.0 of +this cookbook. + +See [CHEF-3493](http://tickets.opscode.com/browse/CHEF-3493) and +[this code comment](http://bit.ly/VgvCgf) for more information on this +requirement. + +## Platform + +Please refer to the [TESTING file](TESTING.md) to see the currently (and passing) tested platforms. The release was tested on: +* Ubuntu 10.04 +* Ubuntu 12.04 +* Ubuntu 13.04 +* Debian 7.1 +* Debian 6.0 (have with manual testing) + +May work with or without modification on other Debian derivatives. + +Recipes +======= + +## default + +This recipe installs the `update-notifier-common` package to provide +the timestamp file used to only run `apt-get update` if the cache is +more than one day old. + +This recipe should appear first in the run list of Debian or Ubuntu +nodes to ensure that the package cache is up to date before managing +any `package` resources with Chef. + +This recipe also sets up a local cache directory for preseeding packages. + +## cacher-client + +Configures the node to use the `apt-cacher-ng` server as a client. + +## cacher-ng + +Installs the `apt-cacher-ng` package and service so the system can +provide APT caching. You can check the usage report at +http://{hostname}:3142/acng-report.html. + +If you wish to help the `cacher-ng` recipe seed itself, you must now explicitly +include the `cacher-client` recipe in your run list **after** `cacher-ng` or you +will block your ability to install any packages (ie. `apt-cacher-ng`). + +Attributes +========== + +* `['apt']['cacher_ipaddress']` - use a cacher server (or standard proxy server) not available via search +* `['apt']['cacher_port']` - port for the cacher-ng service (either client or server), default is '3142' +* `['apt']['cacher_dir']` - directory used by cacher-ng service, default is '/var/cache/apt-cacher-ng' +* `['apt']['cacher-client']['restrict_environment']` - restrict your node to using the `apt-cacher-ng` server in your Environment, default is 'false' +* `['apt']['compiletime']` - force the `cacher-client` recipe to run before other recipes. It forces apt to use the proxy before other recipes run. Useful if your nodes have limited access to public apt repositories. This is overridden if the `cacher-ng` recipe is in your run list. Default is 'false' + +Resources/Providers +=================== + +## Managing repositories + +This LWRP provides an easy way to manage additional APT repositories. +Adding a new repository will notify running the `execute[apt-get-update]` +resource immediately. + +### Actions + +- :add: creates a repository file and builds the repository listing +- :remove: removes the repository file + +### Attribute Parameters + +- repo_name: name attribute. The name of the channel to discover +- uri: the base of the Debian distribution +- distribution: this is usually your release's codename...ie something + like `karmic`, `lucid` or `maverick` +- components: package groupings..when it doubt use `main` +- arch: constrain package to a particular arch like `i386`, `amd64` or + even `armhf` or `powerpc`. Defaults to nil. +- deb_src: whether or not to add the repository as a source repo as + well - value can be `true` or `false`, default `false`. +- keyserver: the GPG keyserver where the key for the repo should be retrieved +- key: if a `keyserver` is provided, this is assumed to be the + fingerprint, otherwise it can be either the URI to the GPG key for + the repo, or a cookbook_file. +- key_proxy: if set, pass the specified proxy via `http-proxy=` to GPG. +- cookbook: if key should be a cookbook_file, specify a cookbook where + the key is located for files/default. Defaults to nil, so it will + use the cookbook where the resource is used. + +### Examples + + # add the Zenoss repo + apt_repository "zenoss" do + uri "http://dev.zenoss.org/deb" + components ["main","stable"] + end + + # add the Nginx PPA; grab key from keyserver + apt_repository "nginx-php" do + uri "http://ppa.launchpad.net/nginx/php5/ubuntu" + distribution node['lsb']['codename'] + components ["main"] + keyserver "keyserver.ubuntu.com" + key "C300EE8C" + end + + # add the Nginx PPA; grab key from keyserver, also add source repo + apt_repository "nginx-php" do + uri "http://ppa.launchpad.net/nginx/php5/ubuntu" + distribution node['lsb']['codename'] + components ["main"] + keyserver "keyserver.ubuntu.com" + key "C300EE8C" + deb_src true + end + + # add the Cloudera Repo of CDH4 packages for Ubuntu 12.04 on AMD64 + apt_repository "cloudera" do + uri "http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh" + arch "amd64" + distribution "precise-cdh4" + components ["contrib"] + key "http://archive.cloudera.com/debian/archive.key" + end + + # remove Zenoss repo + apt_repository "zenoss" do + action :remove + end + +## Pinning packages + +This LWRP provides an easy way to pin packages in /etc/apt/preferences.d. +Although apt-pinning is quite helpful from time to time please note that Debian +does not encourage its use without thorough consideration. + +Further information regarding apt-pinning is available via +http://wiki.debian.org/AptPreferences. + +### Actions + +- :add: creates a preferences file under /etc/apt/preferences.d +- :remove: Removes the file, therefore unpin the package + +### Attribute Parameters + +- package_name: name attribute. The name of the package +- glob: Pin by glob() expression or regexp surrounded by /. +- pin: The package version/repository to pin +- pin_priority: The pinning priority aka "the highest package version wins" + +### Examples + + # Pin libmysqlclient16 to version 5.1.49-3 + apt_preference "libmysqlclient16" do + pin "version 5.1.49-3" + pin_priority "700" + end + + # Unpin libmysqlclient16 + apt_preference "libmysqlclient16" do + action :remove + end + + # Pin all packages from dotdeb.org + apt_preference "dotdeb" do + glob "*" + pin "origin packages.dotdeb.org " + pin_priority "700" + end + +Usage +===== + +Put `recipe[apt]` first in the run list. If you have other recipes +that you want to use to configure how apt behaves, like new sources, +notify the execute resource to run, e.g.: + + template "/etc/apt/sources.list.d/my_apt_sources.list" do + notifies :run, resources(:execute => "apt-get update"), :immediately + end + +The above will run during execution phase since it is a normal +template resource, and should appear before other package resources +that need the sources in the template. + +Put `recipe[apt::cacher-ng]` in the run_list for a server to provide +APT caching and add `recipe[apt::cacher-client]` on the rest of the +Debian-based nodes to take advantage of the caching server. + +If you want to cleanup unused packages, there is also the `apt-get autoclean` +and `apt-get autoremove` resources provided for automated cleanup. + +License and Author +================== + +| | | +|:---------------------|:----------------------------------------| +| **Author** | Joshua Timberman | +| **Author** | Matt Ray () | +| **Author** | Seth Chisamore () | +| | | +| **Copyright** | Copyright (c) 2009-2013, Opscode, Inc. | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/apt/TESTING.md b/chef/cookbooks/apt/TESTING.md new file mode 100644 index 0000000..a875cdc --- /dev/null +++ b/chef/cookbooks/apt/TESTING.md @@ -0,0 +1,44 @@ +This cookbook includes support for running tests via Test Kitchen (1.0) and ChefSpec. This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test + +This cookbook has the following Test-Kitchen coverage: + +| Test Coverage | Ubuntu 10.04 | Ubuntu 12.04 | Ubuntu 13.04 | Debian 7.1 | +| ---------------- |:-------------:|:------------:|:------------:|:----------:| +| default | **Y** | **Y** | **Y** | **Y** | +| cacher-client | **Y** | **Y** | **Y** | **Y** | +| cacher-ng | **Y** | **Y** | **Y** | **Y** | +| cacher-ng-client | **Y** | **Y** | **Y** | **Y** | +| lwrps | **Y** | **Y** | **Y** | **Y** | + +If you wish to run unit tests with ChefSpec, install this additional requirement: + + gem install chefspec + +and you can run the tests with: + + rspec + diff --git a/chef/cookbooks/apt/attributes/default.rb b/chef/cookbooks/apt/attributes/default.rb new file mode 100644 index 0000000..0f919e8 --- /dev/null +++ b/chef/cookbooks/apt/attributes/default.rb @@ -0,0 +1,6 @@ +default['apt']['cacher-client']['restrict_environment'] = false +default['apt']['cacher_dir'] = '/var/cache/apt-cacher-ng' +default['apt']['cacher_port'] = 3142 +default['apt']['caching_server'] = false +default['apt']['compiletime'] = false +default['apt']['key_proxy'] = '' diff --git a/chef/cookbooks/apt/files/default/apt-proxy-v2.conf b/chef/cookbooks/apt/files/default/apt-proxy-v2.conf new file mode 100644 index 0000000..6954004 --- /dev/null +++ b/chef/cookbooks/apt/files/default/apt-proxy-v2.conf @@ -0,0 +1,50 @@ +[DEFAULT] +;; All times are in seconds, but you can add a suffix +;; for minutes(m), hours(h) or days(d) + +;; commented out address so apt-proxy will listen on all IPs +;; address = 127.0.0.1 +port = 9999 +cache_dir = /var/cache/apt-proxy + +;; Control files (Packages/Sources/Contents) refresh rate +min_refresh_delay = 1s +complete_clientless_downloads = 1 + +;; Debugging settings. +debug = all:4 db:0 + +time = 30 +passive_ftp = on + +;;-------------------------------------------------------------- +;; Cache housekeeping + +cleanup_freq = 1d +max_age = 120d +max_versions = 3 + +;;--------------------------------------------------------------- +;; Backend servers +;; +;; Place each server in its own [section] + +[ubuntu] +; Ubuntu archive +backends = + http://us.archive.ubuntu.com/ubuntu + +[ubuntu-security] +; Ubuntu security updates +backends = http://security.ubuntu.com/ubuntu + +[debian] +;; Backend servers, in order of preference +backends = + http://debian.osuosl.org/debian/ + +[security] +;; Debian security archive +backends = + http://security.debian.org/debian-security + http://ftp2.de.debian.org/debian-security diff --git a/chef/cookbooks/apt/metadata.rb b/chef/cookbooks/apt/metadata.rb new file mode 100644 index 0000000..de988f3 --- /dev/null +++ b/chef/cookbooks/apt/metadata.rb @@ -0,0 +1,30 @@ +name "apt" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures apt and apt services and LWRPs for managing apt repositories and preferences" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "2.1.1" +recipe "apt", "Runs apt-get update during compile phase and sets up preseed directories" +recipe "apt::cacher-ng", "Set up an apt-cacher-ng caching proxy" +recipe "apt::cacher-client", "Client for the apt::cacher-ng caching proxy" + +%w{ ubuntu debian }.each do |os| + supports os +end + +attribute "apt/cacher-client/restrict_environment", + :description => "Whether to restrict the search for the caching server to the same environment as this node", + :default => "false" + +attribute "apt/cacher_port", + :description => "Default listen port for the caching server", + :default => "3142" + +attribute "apt/key_proxy", + :description => "Passed as the proxy passed to GPG for the apt_repository resource", + :default => "" + +attribute "apt/caching_server", + :description => "Set this to true if the node is a caching server", + :default => "false" diff --git a/chef/cookbooks/apt/providers/preference.rb b/chef/cookbooks/apt/providers/preference.rb new file mode 100644 index 0000000..8f34e74 --- /dev/null +++ b/chef/cookbooks/apt/providers/preference.rb @@ -0,0 +1,61 @@ +# +# Cookbook Name:: apt +# Provider:: preference +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Build preferences.d file contents +def build_pref(package_name, pin, pin_priority) + preference_content = "Package: #{package_name}\nPin: #{pin}\nPin-Priority: #{pin_priority}\n" +end + +action :add do + new_resource.updated_by_last_action(false) + + preference = build_pref(new_resource.glob || new_resource.package_name, + new_resource.pin, + new_resource.pin_priority) + + preference_dir = directory "/etc/apt/preferences.d" do + owner "root" + group "root" + mode 00755 + recursive true + action :nothing + end + + preference_file = file "/etc/apt/preferences.d/#{new_resource.name}" do + owner "root" + group "root" + mode 00644 + content preference + action :nothing + end + + preference_dir.run_action(:create) + # write out the preference file, replace it if it already exists + preference_file.run_action(:create) +end + +action :remove do + if ::File.exists?("/etc/apt/preferences.d/#{new_resource.name}") + Chef::Log.info "Un-pinning #{new_resource.name} from /etc/apt/preferences.d/" + file "/etc/apt/preferences.d/#{new_resource.name}" do + action :delete + end + new_resource.updated_by_last_action(true) + end +end diff --git a/chef/cookbooks/apt/providers/repository.rb b/chef/cookbooks/apt/providers/repository.rb new file mode 100644 index 0000000..b3e0528 --- /dev/null +++ b/chef/cookbooks/apt/providers/repository.rb @@ -0,0 +1,136 @@ +# +# Cookbook Name:: apt +# Provider:: repository +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +use_inline_resources if defined?(use_inline_resources) + +def whyrun_supported? + true +end + +# install apt key from keyserver +def install_key_from_keyserver(key, keyserver) + execute "install-key #{key}" do + if !node['apt']['key_proxy'].empty? + command "apt-key adv --keyserver-options http-proxy=#{node['apt']['key_proxy']} --keyserver hkp://#{keyserver}:80 --recv #{key}" + else + command "apt-key adv --keyserver #{keyserver} --recv #{key}" + end + action :run + not_if do + extract_fingerprints_from_cmd("apt-key finger").any? do |fingerprint| + fingerprint.end_with?(key.upcase) + end + end + end +end + +# run command and extract gpg ids +def extract_fingerprints_from_cmd(cmd) + so = Mixlib::ShellOut.new(cmd) + so.run_command + so.stdout.split(/\n/).collect do |t| + if z = t.match(/^ +Key fingerprint = ([0-9A-F ]+)/) + z[1].split.join + end + end.compact +end + +# install apt key from URI +def install_key_from_uri(uri) + key_name = uri.split(/\//).last + cached_keyfile = "#{Chef::Config[:file_cache_path]}/#{key_name}" + if new_resource.key =~ /http/ + remote_file cached_keyfile do + source new_resource.key + mode 00644 + action :create + end + else + cookbook_file cached_keyfile do + source new_resource.key + cookbook new_resource.cookbook + mode 00644 + action :create + end + end + + execute "install-key #{key_name}" do + command "apt-key add #{cached_keyfile}" + action :run + not_if do + installed_keys = extract_fingerprints_from_cmd("apt-key finger") + proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint #{cached_keyfile}") + (installed_keys & proposed_keys).sort == proposed_keys.sort + end + end +end + +# build repo file contents +def build_repo(uri, distribution, components, arch, add_deb_src) + components = components.join(' ') if components.respond_to?(:join) + repo_info = "#{uri} #{distribution} #{components}\n" + repo_info = "[arch=#{arch}] #{repo_info}" if arch + repo = "deb #{repo_info}" + repo << "deb-src #{repo_info}" if add_deb_src + repo +end + +action :add do + # add key + if new_resource.keyserver && new_resource.key + install_key_from_keyserver(new_resource.key, new_resource.keyserver) + elsif new_resource.key + install_key_from_uri(new_resource.key) + end + + file "/var/lib/apt/periodic/update-success-stamp" do + action :nothing + end + + execute "apt-get update" do + ignore_failure true + action :nothing + end + + # build repo file + repository = build_repo(new_resource.uri, + new_resource.distribution, + new_resource.components, + new_resource.arch, + new_resource.deb_src) + + file "/etc/apt/sources.list.d/#{new_resource.name}.list" do + owner "root" + group "root" + mode 00644 + content repository + action :create + notifies :delete, "file[/var/lib/apt/periodic/update-success-stamp]", :immediately + notifies :run, "execute[apt-get update]", :immediately if new_resource.cache_rebuild + end +end + +action :remove do + if ::File.exists?("/etc/apt/sources.list.d/#{new_resource.name}.list") + Chef::Log.info "Removing #{new_resource.name} repository from /etc/apt/sources.list.d/" + file "/etc/apt/sources.list.d/#{new_resource.name}.list" do + action :delete + end + end +end diff --git a/chef/cookbooks/apt/recipes/cacher-client.rb b/chef/cookbooks/apt/recipes/cacher-client.rb new file mode 100644 index 0000000..542191d --- /dev/null +++ b/chef/cookbooks/apt/recipes/cacher-client.rb @@ -0,0 +1,70 @@ +# +# Cookbook Name:: apt +# Recipe:: cacher-client +# +# Copyright 2011-2013 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#remove Acquire::http::Proxy lines from /etc/apt/apt.conf since we use 01proxy +#these are leftover from preseed installs +execute 'Remove proxy from /etc/apt/apt.conf' do + command "sed --in-place '/^Acquire::http::Proxy/d' /etc/apt/apt.conf" + only_if "grep Acquire::http::Proxy /etc/apt/apt.conf" +end + +servers = [] +if node['apt'] + if node['apt']['cacher_ipaddress'] + cacher = Chef::Node.new + cacher.default.name = node['apt']['cacher_ipaddress'] + cacher.default.ipaddress = node['apt']['cacher_ipaddress'] + cacher.default.apt.cacher_port = node['apt']['cacher_port'] + servers << cacher + elsif node['apt']['caching_server'] + node.override['apt']['compiletime'] = false + servers << node + end +end + +unless (Chef::Config[:solo] || servers.length > 0) + query = "apt_caching_server:true" + query += " AND chef_environment:#{node.chef_environment}" if node['apt']['cacher-client']['restrict_environment'] + Chef::Log.debug("apt::cacher-client searching for '#{query}'") + servers += search(:node, query) +end + +if servers.length > 0 + Chef::Log.info("apt-cacher-ng server found on #{servers[0]}.") + t = template '/etc/apt/apt.conf.d/01proxy' do + source '01proxy.erb' + owner 'root' + group 'root' + mode 00644 + variables( + :proxy => servers[0]['ipaddress'], + :port => servers[0]['apt']['cacher_port'] + ) + action( node['apt']['compiletime'] ? :nothing : :create ) + notifies :run, 'execute[apt-get update]', :immediately + end + t.run_action(:create) if node['apt']['compiletime'] +else + Chef::Log.info('No apt-cacher-ng server found.') + file '/etc/apt/apt.conf.d/01proxy' do + action :delete + end +end + +include_recipe 'apt::default' diff --git a/chef/cookbooks/apt/recipes/cacher-ng.rb b/chef/cookbooks/apt/recipes/cacher-ng.rb new file mode 100644 index 0000000..30e7af3 --- /dev/null +++ b/chef/cookbooks/apt/recipes/cacher-ng.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: apt +# Recipe:: cacher-ng +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +node.set['apt']['caching_server'] = true + +package "apt-cacher-ng" do + action :install +end + +directory node['apt']['cacher_dir'] do + owner "apt-cacher-ng" + group "apt-cacher-ng" + mode 0755 +end + +template "/etc/apt-cacher-ng/acng.conf" do + source "acng.conf.erb" + owner "root" + group "root" + mode 00644 + notifies :restart, "service[apt-cacher-ng]", :immediately +end + +service "apt-cacher-ng" do + supports :restart => true, :status => false + action [:enable, :start] +end diff --git a/chef/cookbooks/apt/recipes/default.rb b/chef/cookbooks/apt/recipes/default.rb new file mode 100644 index 0000000..a6bd8c4 --- /dev/null +++ b/chef/cookbooks/apt/recipes/default.rb @@ -0,0 +1,68 @@ +# +# Cookbook Name:: apt +# Recipe:: default +# +# Copyright 2008-2011, Opscode, Inc. +# Copyright 2009, Bryan McLellan +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Run apt-get update to create the stamp file +execute "apt-get-update" do + command "apt-get update" + ignore_failure true + not_if do ::File.exists?('/var/lib/apt/periodic/update-success-stamp') end +end + +# For other recipes to call to force an update +execute "apt-get update" do + command "apt-get update" + ignore_failure true + action :nothing +end + +# Automatically remove packages that are no longer needed for dependencies +execute "apt-get autoremove" do + command "apt-get -y autoremove" + action :nothing +end + +# Automatically remove .deb files for packages no longer on your system +execute "apt-get autoclean" do + command "apt-get -y autoclean" + action :nothing +end + +# provides /var/lib/apt/periodic/update-success-stamp on apt-get update +package "update-notifier-common" do + notifies :run, 'execute[apt-get-update]', :immediately +end + +execute "apt-get-update-periodic" do + command "apt-get update" + ignore_failure true + only_if do + ::File.exists?('/var/lib/apt/periodic/update-success-stamp') && + ::File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400 + end +end + +%w{/var/cache/local /var/cache/local/preseeding}.each do |dirname| + directory dirname do + owner "root" + group "root" + mode 00755 + action :create + end +end diff --git a/chef/cookbooks/apt/resources/preference.rb b/chef/cookbooks/apt/resources/preference.rb new file mode 100644 index 0000000..3ad7207 --- /dev/null +++ b/chef/cookbooks/apt/resources/preference.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: apt +# Resource:: preference +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :remove + +def initialize(*args) + super + @action = :add +end + +attribute :package_name, :kind_of => String, :name_attribute => true +attribute :glob, :kind_of => String +attribute :pin, :kind_of => String +attribute :pin_priority, :kind_of => String diff --git a/chef/cookbooks/apt/resources/repository.rb b/chef/cookbooks/apt/resources/repository.rb new file mode 100644 index 0000000..7515da4 --- /dev/null +++ b/chef/cookbooks/apt/resources/repository.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: apt +# Resource:: repository +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :remove + +def initialize(*args) + super + @action = :add +end + +#name of the repo, used for source.list filename +attribute :repo_name, :kind_of => String, :name_attribute => true +attribute :uri, :kind_of => String +attribute :distribution, :kind_of => String +attribute :components, :kind_of => Array, :default => [] +attribute :arch, :kind_of => String, :default => nil +#whether or not to add the repository as a source repo as well +attribute :deb_src, :default => false +attribute :keyserver, :kind_of => String, :default => nil +attribute :key, :kind_of => String, :default => nil +attribute :cookbook, :kind_of => String, :default => nil +#trigger cache rebuild +#If not you can trigger in the recipe itself after checking the status of resource.updated{_by_last_action}? +attribute :cache_rebuild, :kind_of => [TrueClass, FalseClass], :default => true diff --git a/chef/cookbooks/apt/spec/cacher-client_spec.rb b/chef/cookbooks/apt/spec/cacher-client_spec.rb new file mode 100644 index 0000000..923d9cb --- /dev/null +++ b/chef/cookbooks/apt/spec/cacher-client_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe 'apt::cacher-client' do + + context 'no server' do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new + runner.converge('apt::cacher-client') + end + + it 'does not create 01proxy file' do + expect(chef_run).not_to create_file('/etc/apt/apt.conf.d/01proxy') + end + end + + context 'server provided' do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new + runner.node.set['apt']['cacher_ipaddress'] = '22.33.44.55' + runner.node.set['apt']['cacher_port'] = '9876' + runner.converge('apt::cacher-client') + end + + it 'creates 01proxy file' do + expect(chef_run).to create_file('/etc/apt/apt.conf.d/01proxy') + expect(chef_run).to create_file_with_content('/etc/apt/apt.conf.d/01proxy', 'Acquire::http::Proxy "http://22.33.44.55:9876";') + end + + end + +end diff --git a/chef/cookbooks/apt/spec/cacher-ng_spec.rb b/chef/cookbooks/apt/spec/cacher-ng_spec.rb new file mode 100644 index 0000000..16dc724 --- /dev/null +++ b/chef/cookbooks/apt/spec/cacher-ng_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'apt::cacher-ng' do + + context 'server' do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new + runner.node.set['apt']['cacher_port'] = '9876' + runner.converge('apt::cacher-ng') + end + + it 'installs apt-cacher-ng' do + expect(chef_run).to install_package('apt-cacher-ng') + end + + it 'creates acng.conf file' do + expect(chef_run).to create_file('/etc/apt-cacher-ng/acng.conf') + end + + it 'enables and starts apt-cacher-ng' do + expect(chef_run).to set_service_to_start_on_boot 'apt-cacher-ng' + expect(chef_run).to start_service 'apt-cacher-ng' + end + + end + +end diff --git a/chef/cookbooks/apt/spec/default_spec.rb b/chef/cookbooks/apt/spec/default_spec.rb new file mode 100644 index 0000000..dde466a --- /dev/null +++ b/chef/cookbooks/apt/spec/default_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe 'apt::default' do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new() + runner.converge('apt::default') + end + + it 'installs update-notifier-common' do + expect(chef_run).to install_package 'update-notifier-common' + end + + it 'apt-get updates' do + expect(chef_run).to execute_command 'apt-get update' + end + + it 'creates preseeding directory' do + expect(chef_run).to create_directory('/var/cache/local') + expect(chef_run).to create_directory('/var/cache/local/preseeding') + end + +end diff --git a/chef/cookbooks/apt/spec/spec_helper.rb b/chef/cookbooks/apt/spec/spec_helper.rb new file mode 100644 index 0000000..cab2b59 --- /dev/null +++ b/chef/cookbooks/apt/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'chefspec' + +RSpec.configure do |config| + config.color_enabled = true + config.tty = true + config.formatter = :documentation + config.treat_symbols_as_metadata_keys_with_true_values = true + config.filter_run :focus => true + config.run_all_when_everything_filtered = true + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/chef/cookbooks/apt/templates/debian-6.0/acng.conf.erb b/chef/cookbooks/apt/templates/debian-6.0/acng.conf.erb new file mode 100644 index 0000000..98a681c --- /dev/null +++ b/chef/cookbooks/apt/templates/debian-6.0/acng.conf.erb @@ -0,0 +1,173 @@ +# Letter case in directive names does not matter. Must be separated with colons. +# Valid boolean values are a zero number for false, non-zero numbers for true. + +CacheDir: <%= node['apt']['cacher_dir'] %> + +# set empty to disable logging +LogDir: /var/log/apt-cacher-ng + +# TCP (http) port +# Set to 9999 to emulate apt-proxy +Port:<%= node['apt']['cacher_port'] %> + +# Addresses or hostnames to listen on. Multiple addresses must be separated by +# spaces. Each entry must be associated with a local interface. DNS resolution +# is performed using getaddrinfo(3) for all available protocols (i.e. IPv4 and +# IPv6 if available). +# +# Default: not set, will listen on all interfaces. +# +# BindAddress: localhost 192.168.7.254 publicNameOnMainInterface + +#Proxy: http://www-proxy.example.net:80 +#proxy: http://username:proxypassword@proxy.example.net:3128 + +# Repository remapping. See manual for details. +# In this example, backends file is generated during package installation. +Remap-debrep: file:deb_mirror*.gz /debian ; file:backends_debian +Remap-uburep: file:ubuntu_mirrors /ubuntu ; file:backends_ubuntu +Remap-debvol: file:debvol_mirror*.gz /debian-volatile ; file:backends_debvol +Remap-cygwin: file:cygwin_mirrors /cygwin # ; file:backends_cygwin # incomplete, please create this file + +# Virtual page accessible in a web browser to see statistics and status +# information, i.e. under http://localhost:3142/acng-report.html +ReportPage: acng-report.html + +# Socket file for accessing through local UNIX socket instead of TCP/IP. Can be +# used with inetd bridge or cron client. +# SocketPath:/var/run/apt-cacher-ng/socket + +# Forces log file to be written to disk after every line when set to 1. Default +# is 0, buffer flush happens after client disconnects. +# +# (technically, this is an alias to the Debug option provided for convenience) +# +# UnbufferLogs: 0 + +# Set to 0 to store only type, time and transfer sizes. +# 1 -> client IP and relative local path are logged too +# VerboseLog: 1 + +# Don't detach from the console +# ForeGround: 0 + +# Store the pid of the daemon process therein +# PidFile: /var/run/apt-cacher-ng/pid + +# Forbid outgoing connections, work around them or respond with 503 error +# offlinemode:0 + +# Forbid all downloads that don't run through preconfigured backends (.where) +#ForceManaged: 0 + +# Days before considering an unreferenced file expired (to be deleted). +# Warning: if the value is set too low and particular index files are not +# available for some days (mirror downtime) there is a risk of deletion of +# still usefull package files. +ExTreshold: 4 + +# Stop expiration when a critical problem appeared. Currently only failed +# refresh of an index file is considered as critical. +# +# WARNING: don't touch this option or set to a non-zero number. +# Anything else is DANGEROUS and may cause data loss. +# +# ExAbortOnProblems: 1 + +# Replace some Windows/DOS-FS incompatible chars when storing +# StupidFs: 0 + +# Experimental feature for apt-listbugs: pass-through SOAP requests and +# responses to/from bugs.debian.org. If not set, default is true if +# ForceManaged is enabled and false otherwise. +# ForwardBtsSoap: 1 + +# The daemon has a small cache for DNS data, to speed up resolution. The +# expiration time of the DNS entries can be configured in seconds. +# DnsCacheSeconds: 3600 + +# Don't touch the following values without good consideration! +# +# Max. count of connection threads kept ready (for faster response in the +# future). Should be a sane value between 0 and average number of connections, +# and depend on the amount of spare RAM. +# MaxStandbyConThreads: 8 +# +# Hard limit of active thread count for incomming connections, i.e. operation +# is refused when this value is reached (below zero = unlimited). +# MaxConThreads: -1 +# +#VfilePattern = (^|.*?/)(Index|Packages\.bz2|Packages\.gz|Packages|Release|Release\.gpg|Sources\.bz2|Sources\.gz|Sources|release|index\.db-.*\.gz|Contents-[^/]*\.gz|pkglist[^/]*\.bz2|rclist[^/]*\.bz2|/meta-release[^/]*|Translation[^/]*\.bz2)$ +#PfilePattern = .*(\.deb|\.rpm|\.dsc|\.tar\.gz\.gpg|\.tar\.gz|\.diff\.gz|\.diff\.bz2|\.jigdo|\.template|changelog|copyright|\.udeb|\.diff/.*\.gz|vmlinuz|initrd\.gz|(Devel)?ReleaseAnnouncement(\\?.*)?)$ +# Whitelist for expiration, file types not to be removed even when being +# unreferenced. Default: same as VfilePattern which is a safe bed. When and +# only when the only used mirrors are official repositories (with working +# Release files) then it might be set to something more restrictive, like +# (^|.*?/)(Release|Release\.gpg|release|meta-release|Translation[^/]*\.bz2)$ +#WfilePattern = (^|.*?/)(Index|Packages\.bz2|Packages\.gz|Packages|Release|Release\.gpg|Sources\.bz2|Sources\.gz|Sources|release|index\.db-.*\.gz|Contents-[^/]*\.gz|pkglist[^/]*\.bz2|rclist[^/]*\.bz2|/meta-release[^/]*|Translation[^/]*\.bz2)$ + +# Higher modes only working with the debug version +# Warning, writes a lot into apt-cacher.err logfile +# Value overwrites UnbufferLogs setting (aliased) +# Debug:3 + +# Usually, general purpose proxies like Squid expose the IP adress of the +# client user to the remote server using the X-Forwarded-For HTTP header. This +# behaviour can be optionally turned on with the Expose-Origin option. +# ExposeOrigin: 0 + +# When logging the originating IP address, trust the information supplied by +# the client in the X-Forwarded-For header. +# LogSubmittedOrigin: 0 + +# The version string reported to the peer, to be displayed as HTTP client (and +# version) in the logs of the mirror. +# WARNING: some archives use this header to detect/guess capabilities of the +# client (i.e. redirection support) and change the behaviour accordingly, while +# ACNG might not support the expected features. Expect side effects. +# +# UserAgent: Yet Another HTTP Client/1.2.3p4 + +# In some cases the Import and Expiration tasks might create fresh volatile +# data for internal use by reconstructing them using patch files. This +# by-product might be recompressed with bzip2 and with some luck the resulting +# file becomes identical to the *.bz2 file on the server, usable for APT +# clients trying to fetch the full .bz2 compressed version. Injection of the +# generated files into the cache has however a disadvantage on underpowered +# servers: bzip2 compession can create high load on the server system and the +# visible download of the busy .bz2 files also becomes slower. +# +# RecompBz2: 0 + +# Network timeout for outgoing connections. +# NetworkTimeout: 60 + +# Sometimes it makes sense to not store the data in cache and just return the +# package data to client as it comes in. DontCache parameters can enable this +# behaviour for certain URL types. The tokens are extended regular expressions +# that URLs are matched against. +# +# DontCacheRequested is applied to the URL as it comes in from the client. +# Example: exclude packages built with kernel-package for x86 +# DontCacheRequested: linux-.*_10\...\.Custo._i386 +# Example usecase: exclude popular private IP ranges from caching +# DontCacheRequested: 192.168.0 ^10\..* 172.30 +# +# DontCacheResolved is applied to URLs after mapping to the target server. If +# multiple backend servers are specified then it's only matched against the +# download link for the FIRST possible source (due to implementation limits). +# Example usecase: all Ubuntu stuff comes from a local mirror (specified as +# backend), don't cache it again: +# DontCacheResolved: ubuntumirror.local.net +# +# DontCache directive sets (overrides) both, DontCacheResolved and +# DontCacheRequested. Provided for convenience, see those directives for +# details. +# +# Default permission set of freshly created files and directories, as octal +# numbers (see chmod(1) for details). +# Can by limited by the umask value (see umask(2) for details) if it's set in +# the environment of the starting shell, e.g. in apt-cacher-ng init script or +# in its configuration file. +# DirPerms: 00755 +# FilePerms: 00664 diff --git a/chef/cookbooks/apt/templates/default/01proxy.erb b/chef/cookbooks/apt/templates/default/01proxy.erb new file mode 100644 index 0000000..eea71c2 --- /dev/null +++ b/chef/cookbooks/apt/templates/default/01proxy.erb @@ -0,0 +1,2 @@ +Acquire::http::Proxy "http://<%= @proxy %>:<%= @port %>"; +Acquire::https::Proxy "DIRECT"; diff --git a/chef/cookbooks/apt/templates/default/acng.conf.erb b/chef/cookbooks/apt/templates/default/acng.conf.erb new file mode 100644 index 0000000..3aa0c92 --- /dev/null +++ b/chef/cookbooks/apt/templates/default/acng.conf.erb @@ -0,0 +1,275 @@ +# Letter case in directive names does not matter. Must be separated with colons. +# Valid boolean values are a zero number for false, non-zero numbers for true. + +CacheDir: <%= node['apt']['cacher_dir'] %> + +# set empty to disable logging +LogDir: /var/log/apt-cacher-ng + +# place to look for additional configuration and resource files if they are not +# found in the configuration directory +# SupportDir: /usr/lib/apt-cacher-ng + +# TCP (http) port +# Set to 9999 to emulate apt-proxy +Port:<%= node['apt']['cacher_port'] %> + +# Addresses or hostnames to listen on. Multiple addresses must be separated by +# spaces. Each entry must be an exact local address which is associated with a +# local interface. DNS resolution is performed using getaddrinfo(3) for all +# available protocols (IPv4, IPv6, ...). Using a protocol specific format will +# create binding(s) only on protocol specific socket(s) (e.g. 0.0.0.0 will listen +# only to IPv4). +# +# Default: not set, will listen on all interfaces and protocols +# +# BindAddress: localhost 192.168.7.254 publicNameOnMainInterface + +# The specification of another proxy which shall be used for downloads. +# Username and password are, and see manual for limitations. +# +#Proxy: http://www-proxy.example.net:80 +#proxy: username:proxypassword@proxy.example.net:3128 + +# Repository remapping. See manual for details. +# In this example, some backends files might be generated during package +# installation using information collected on the system. +Remap-debrep: file:deb_mirror*.gz /debian ; file:backends_debian # Debian Archives +Remap-uburep: file:ubuntu_mirrors /ubuntu ; file:backends_ubuntu # Ubuntu Archives +Remap-debvol: file:debvol_mirror*.gz /debian-volatile ; file:backends_debvol # Debian Volatile Archives +Remap-cygwin: file:cygwin_mirrors /cygwin # ; file:backends_cygwin # incomplete, please create this file or specify preferred mirrors here +Remap-sfnet: file:sfnet_mirrors # ; file:backends_sfnet # incomplete, please create this file or specify preferred mirrors here +Remap-alxrep: file:archlx_mirrors /archlinux # ; file:backend_archlx # Arch Linux +Remap-fedora: file:fedora_mirrors # Fedora Linux +Remap-epel: file:epel_mirrors # Fedora EPEL +Remap-slrep: file:sl_mirrors # Scientific Linux + +# This is usually not needed for security.debian.org because it's always the +# same DNS hostname. However, it might be enabled in order to use hooks, +# ForceManaged mode or special flags in this context. +# Remap-secdeb: security.debian.org + +# Virtual page accessible in a web browser to see statistics and status +# information, i.e. under http://localhost:3142/acng-report.html +ReportPage: acng-report.html + +# Socket file for accessing through local UNIX socket instead of TCP/IP. Can be +# used with inetd bridge or cron client. +# SocketPath:/var/run/apt-cacher-ng/socket + +# Forces log file to be written to disk after every line when set to 1. Default +# is 0, buffers are flushed when the client disconnects. +# +# (technically, alias to the Debug option, see its documentation for details) +# +# UnbufferLogs: 0 + +# Set to 0 to store only type, time and transfer sizes. +# 1 -> client IP and relative local path are logged too +# VerboseLog: 1 + +# Don't detach from the console +# ForeGround: 0 + +# Store the pid of the daemon process therein +# PidFile: /var/run/apt-cacher-ng/pid + +# Forbid outgoing connections, work around them or respond with 503 error +# offlinemode:0 + +# Forbid all downloads that don't run through preconfigured backends (.where) +#ForceManaged: 0 + +# Days before considering an unreferenced file expired (to be deleted). +# Warning: if the value is set too low and particular index files are not +# available for some days (mirror downtime) there is a risk of deletion of +# still useful package files. +ExTreshold: 4 + +# Stop expiration when a critical problem appeared. Currently only failed +# refresh of an index file is considered as critical. +# +# WARNING: don't touch this option or set to zero. +# Anything else is DANGEROUS and may cause data loss. +# +# ExAbortOnProblems: 1 + +# Replace some Windows/DOS-FS incompatible chars when storing +# StupidFs: 0 + +# Experimental feature for apt-listbugs: pass-through SOAP requests and +# responses to/from bugs.debian.org. If not set, default is true if +# ForceManaged is enabled and false otherwise. +# ForwardBtsSoap: 1 + +# The daemon has a small cache for DNS data, to speed up resolution. The +# expiration time of the DNS entries can be configured in seconds. +# DnsCacheSeconds: 3600 + +# Don't touch the following values without good consideration! +# +# Max. count of connection threads kept ready (for faster response in the +# future). Should be a sane value between 0 and average number of connections, +# and depend on the amount of spare RAM. +# MaxStandbyConThreads: 8 +# +# Hard limit of active thread count for incoming connections, i.e. operation +# is refused when this value is reached (below zero = unlimited). +# MaxConThreads: -1 +# +# Pigeonholing files with regular expressions (static/volatile). Can be +# overriden here but not should not be done permanently because future update +# of default settings would not be applied later. +# VfilePattern = (^|.*?/)(Index|Packages(\.gz|\.bz2|\.lzma|\.xz)?|InRelease|Release|Release\.gpg|Sources(\.gz|\.bz2|\.lzma|\.xz)?|release|index\.db-.*\.gz|Contents-[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|pkglist[^/]*\.bz2|rclist[^/]*\.bz2|/meta-release[^/]*|Translation[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|MD5SUMS|SHA1SUMS|((setup|setup-legacy)(\.ini|\.bz2|\.hint)(\.sig)?)|mirrors\.lst|repo(index|md)\.xml(\.asc|\.key)?|directory\.yast|products|content(\.asc|\.key)?|media|filelists\.xml\.gz|filelists\.sqlite\.bz2|repomd\.xml|packages\.[a-zA-Z][a-zA-Z]\.gz|info\.txt|license\.tar\.gz|license\.zip|.*\.db(\.tar\.gz)?|.*\.files\.tar\.gz|.*\.abs\.tar\.gz|metalink\?repo|.*prestodelta\.xml\.gz)$|/dists/.*/installer-[^/]+/[^0-9][^/]+/images/.* +# PfilePattern = .*(\.d?deb|\.rpm|\.dsc|\.tar(\.gz|\.bz2|\.lzma|\.xz)(\.gpg)?|\.diff(\.gz|\.bz2|\.lzma|\.xz)|\.jigdo|\.template|changelog|copyright|\.udeb|\.debdelta|\.diff/.*\.gz|(Devel)?ReleaseAnnouncement(\?.*)?|[a-f0-9]+-(susedata|updateinfo|primary|deltainfo).xml.gz|fonts/(final/)?[a-z]+32.exe(\?download.*)?|/dists/.*/installer-[^/]+/[0-9][^/]+/images/.*)$ +# Whitelist for expiration, file types not to be removed even when being +# unreferenced. Default: many parts from VfilePattern where no parent index +# exists or might be unknown. +# WfilePattern = (^|.*?/)(Release|InRelease|Release\.gpg|(Packages|Sources)(\.gz|\.bz2|\.lzma|\.xz)?|Translation[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|MD5SUMS|SHA1SUMS|.*\.xml|.*\.db\.tar\.gz|.*\.files\.tar\.gz|.*\.abs\.tar\.gz|[a-z]+32.exe)$|/dists/.*/installer-.*/images/.* + +# Higher modes only working with the debug version +# Warning, writes a lot into apt-cacher.err logfile +# Value overwrites UnbufferLogs setting (aliased) +# Debug:3 + +# Usually, general purpose proxies like Squid expose the IP address of the +# client user to the remote server using the X-Forwarded-For HTTP header. This +# behaviour can be optionally turned on with the Expose-Origin option. +# ExposeOrigin: 0 + +# When logging the originating IP address, trust the information supplied by +# the client in the X-Forwarded-For header. +# LogSubmittedOrigin: 0 + +# The version string reported to the peer, to be displayed as HTTP client (and +# version) in the logs of the mirror. +# WARNING: some archives use this header to detect/guess capabilities of the +# client (i.e. redirection support) and change the behaviour accordingly, while +# ACNG might not support the expected features. Expect side effects. +# +# UserAgent: Yet Another HTTP Client/1.2.3p4 + +# In some cases the Import and Expiration tasks might create fresh volatile +# data for internal use by reconstructing them using patch files. This +# by-product might be recompressed with bzip2 and with some luck the resulting +# file becomes identical to the *.bz2 file on the server, usable for APT +# clients trying to fetch the full .bz2 compressed version. Injection of the +# generated files into the cache has however a disadvantage on underpowered +# servers: bzip2 compression can create high load on the server system and the +# visible download of the busy .bz2 files also becomes slower. +# +# RecompBz2: 0 + +# Network timeout for outgoing connections. +# NetworkTimeout: 60 + +# Sometimes it makes sense to not store the data in cache and just return the +# package data to client as it comes in. DontCache parameters can enable this +# behaviour for certain URL types. The tokens are extended regular expressions +# that URLs are matched against. +# +# DontCacheRequested is applied to the URL as it comes in from the client. +# Example: exclude packages built with kernel-package for x86 +# DontCacheRequested: linux-.*_10\...\.Custo._i386 +# Example usecase: exclude popular private IP ranges from caching +# DontCacheRequested: 192.168.0 ^10\..* 172.30 +# +# DontCacheResolved is applied to URLs after mapping to the target server. If +# multiple backend servers are specified then it's only matched against the +# download link for the FIRST possible source (due to implementation limits). +# Example usecase: all Ubuntu stuff comes from a local mirror (specified as +# backend), don't cache it again: +# DontCacheResolved: ubuntumirror.local.net +# +# DontCache directive sets (overrides) both, DontCacheResolved and +# DontCacheRequested. Provided for convenience, see those directives for +# details. +# +# Default permission set of freshly created files and directories, as octal +# numbers (see chmod(1) for details). +# Can by limited by the umask value (see umask(2) for details) if it's set in +# the environment of the starting shell, e.g. in apt-cacher-ng init script or +# in its configuration file. +# DirPerms: 00755 +# FilePerms: 00664 +# +# +# It's possible to use use apt-cacher-ng as a regular web server with limited +# feature set, i.e. +# including directory browsing and download of any file; +# excluding sorting, mime types/encodings, CGI execution, index page +# redirection and other funny things. +# To get this behavior, mappings between virtual directories and real +# directories on the server must be defined with the LocalDirs directive. +# Virtual and real dirs are separated by spaces, multiple pairs are separated +# by semi-colons. Real directories must be absolute paths. +# NOTE: Since the names of that key directories share the same namespace as +# repository names (see Remap-...) it's administrators job to avoid such +# collisions on them (unless created deliberately). +# +# LocalDirs: woo /data/debarchive/woody ; hamm /data/debarchive/hamm + +# Precache a set of files referenced by specified index files. This can be used +# to create a partial mirror usable for offline work. There are certain limits +# and restrictions on the path specification, see manual for details. A list of +# (maybe) relevant index files could be retrieved via +# "apt-get --print-uris update" on a client machine. +# +# PrecacheFor: debrep/dists/unstable/*/source/Sources* debrep/dists/unstable/*/binary-amd64/Packages* + +# Arbitrary set of data to append to request headers sent over the wire. Should +# be a well formated HTTP headers part including newlines (DOS style) which +# can be entered as escape sequences (\r\n). +# RequestAppendix: X-Tracking-Choice: do-not-track\r\n + +# Specifies the IP protocol families to use for remote connections. Order does +# matter, first specified are considered first. Possible combinations: +# v6 v4 +# v4 v6 +# v6 +# v4 +# (empty or not set: use system default) +# +# ConnectProto: v6 v4 + +# Regular expiration algorithm finds package files which are no longer listed +# in any index file and removes them of them after a safety period. +# This option allows to keep more versions of a package in the cache after +# safety period is over. +# KeepExtraVersions: 1 + +# Optionally uses TCP access control provided by libwrap, see hosts_access(5) +# for details. Daemon name is apt-cacher-ng. Default if not set: decided on +# startup by looking for explicit mentioning of apt-cacher-ng in +# /etc/hosts.allow or /etc/hosts.deny files. +# UseWrap: 0 + +# If many machines from the same local network attempt to update index files +# (apt-get update) at nearly the same time, the known state of these index file +# is temporarily frozen and multiple requests receive the cached response +# without contacting the server. This parameter (in seconds) specifies the +# length of this period before the files are considered outdated. +# Setting it too low transfers more data and increases remote server load, +# setting it too high (more than a couple of minutes) increases the risk of +# delivering inconsistent responses to the clients. +# FreshIndexMaxAge: 27 + +# Usually the users are not allowed to specify custom TCP ports of remote +# mirrors in the requests, only the default HTTP port can be used (instead, +# proxy administrator can create Remap- rules with custom ports). This +# restriction can be disabled by specifying a list of allowed ports or 0 for +# any port. +# +# AllowUserPorts: 80 + +# Normally the HTTP redirection responses are forwarded to the original caller +# (i.e. APT) which starts a new download attempt from the new URL. This +# solution is ok for client configurations with proxy mode but doesn't work +# well with configurations using URL prefixes. To work around this the server +# can restart its own download with another URL. However, this might be used to +# circumvent download source policies by malicious users. +# The RedirMax option specifies how many such redirects the server should +# follow per request, 0 disables the internal redirection. If not set, +# default value is 0 if ForceManaged is used and 5 otherwise. +# +# RedirMax: 5 diff --git a/chef/cookbooks/apt/templates/ubuntu-10.04/acng.conf.erb b/chef/cookbooks/apt/templates/ubuntu-10.04/acng.conf.erb new file mode 100644 index 0000000..0e7c779 --- /dev/null +++ b/chef/cookbooks/apt/templates/ubuntu-10.04/acng.conf.erb @@ -0,0 +1,269 @@ +# Letter case in directive names does not matter. Must be separated with colons. +# Valid boolean values are a zero number for false, non-zero numbers for true. + +CacheDir: <%= node['apt']['cacher_dir'] %> + +# set empty to disable logging +LogDir: /var/log/apt-cacher-ng + +# place to look for additional configuration and resource files if they are not +# found in the configuration directory +# SupportDir: /usr/lib/apt-cacher-ng + +# TCP (http) port +# Set to 9999 to emulate apt-proxy +Port:<%= node['apt']['cacher_port'] %> + +# Addresses or hostnames to listen on. Multiple addresses must be separated by +# spaces. Each entry must be an exact local address which is associated with a +# local interface. DNS resolution is performed using getaddrinfo(3) for all +# available protocols (IPv4, IPv6, ...). Using a protocol specific format will +# create binding(s) only on protocol specific socket(s) (e.g. 0.0.0.0 will listen +# only to IPv4). +# +# Default: not set, will listen on all interfaces and protocols +# +# BindAddress: localhost 192.168.7.254 publicNameOnMainInterface + +# The specification of another proxy which shall be used for downloads. +# Username and password are, and see manual for limitations. +# +#Proxy: http://www-proxy.example.net:80 +#proxy: username:proxypassword@proxy.example.net:3128 + +# Repository remapping. See manual for details. +# In this example, some backends files might be generated during package +# installation using information collected on the system. +Remap-debrep: file:deb_mirror*.gz /debian ; file:backends_debian # Debian Archives +Remap-uburep: file:ubuntu_mirrors /ubuntu ; file:backends_ubuntu # Ubuntu Archives +Remap-debvol: file:debvol_mirror*.gz /debian-volatile ; file:backends_debvol # Debian Volatile Archives + +# This is usually not needed for security.debian.org because it's always the +# same DNS hostname. However, it might be enabled in order to use hooks, +# ForceManaged mode or special flags in this context. +# Remap-secdeb: security.debian.org + +# Virtual page accessible in a web browser to see statistics and status +# information, i.e. under http://localhost:3142/acng-report.html +ReportPage: acng-report.html + +# Socket file for accessing through local UNIX socket instead of TCP/IP. Can be +# used with inetd bridge or cron client. +# SocketPath:/var/run/apt-cacher-ng/socket + +# Forces log file to be written to disk after every line when set to 1. Default +# is 0, buffers are flushed when the client disconnects. +# +# (technically, alias to the Debug option, see its documentation for details) +# +# UnbufferLogs: 0 + +# Set to 0 to store only type, time and transfer sizes. +# 1 -> client IP and relative local path are logged too +# VerboseLog: 1 + +# Don't detach from the console +# ForeGround: 0 + +# Store the pid of the daemon process therein +# PidFile: /var/run/apt-cacher-ng/pid + +# Forbid outgoing connections, work around them or respond with 503 error +# offlinemode:0 + +# Forbid all downloads that don't run through preconfigured backends (.where) +#ForceManaged: 0 + +# Days before considering an unreferenced file expired (to be deleted). +# Warning: if the value is set too low and particular index files are not +# available for some days (mirror downtime) there is a risk of deletion of +# still useful package files. +ExTreshold: 4 + +# Stop expiration when a critical problem appeared. Currently only failed +# refresh of an index file is considered as critical. +# +# WARNING: don't touch this option or set to zero. +# Anything else is DANGEROUS and may cause data loss. +# +# ExAbortOnProblems: 1 + +# Replace some Windows/DOS-FS incompatible chars when storing +# StupidFs: 0 + +# Experimental feature for apt-listbugs: pass-through SOAP requests and +# responses to/from bugs.debian.org. If not set, default is true if +# ForceManaged is enabled and false otherwise. +# ForwardBtsSoap: 1 + +# The daemon has a small cache for DNS data, to speed up resolution. The +# expiration time of the DNS entries can be configured in seconds. +# DnsCacheSeconds: 3600 + +# Don't touch the following values without good consideration! +# +# Max. count of connection threads kept ready (for faster response in the +# future). Should be a sane value between 0 and average number of connections, +# and depend on the amount of spare RAM. +# MaxStandbyConThreads: 8 +# +# Hard limit of active thread count for incoming connections, i.e. operation +# is refused when this value is reached (below zero = unlimited). +# MaxConThreads: -1 +# +# Pigeonholing files with regular expressions (static/volatile). Can be +# overriden here but not should not be done permanently because future update +# of default settings would not be applied later. +# VfilePattern = (^|.*?/)(Index|Packages(\.gz|\.bz2|\.lzma|\.xz)?|InRelease|Release|Release\.gpg|Sources(\.gz|\.bz2|\.lzma|\.xz)?|release|index\.db-.*\.gz|Contents-[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|pkglist[^/]*\.bz2|rclist[^/]*\.bz2|/meta-release[^/]*|Translation[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|MD5SUMS|SHA1SUMS|((setup|setup-legacy)(\.ini|\.bz2|\.hint)(\.sig)?)|mirrors\.lst|repo(index|md)\.xml(\.asc|\.key)?|directory\.yast|products|content(\.asc|\.key)?|media|filelists\.xml\.gz|filelists\.sqlite\.bz2|repomd\.xml|packages\.[a-zA-Z][a-zA-Z]\.gz|info\.txt|license\.tar\.gz|license\.zip|.*\.db(\.tar\.gz)?|.*\.files\.tar\.gz|.*\.abs\.tar\.gz|metalink\?repo|.*prestodelta\.xml\.gz)$|/dists/.*/installer-[^/]+/[^0-9][^/]+/images/.* +# PfilePattern = .*(\.d?deb|\.rpm|\.dsc|\.tar(\.gz|\.bz2|\.lzma|\.xz)(\.gpg)?|\.diff(\.gz|\.bz2|\.lzma|\.xz)|\.jigdo|\.template|changelog|copyright|\.udeb|\.debdelta|\.diff/.*\.gz|(Devel)?ReleaseAnnouncement(\?.*)?|[a-f0-9]+-(susedata|updateinfo|primary|deltainfo).xml.gz|fonts/(final/)?[a-z]+32.exe(\?download.*)?|/dists/.*/installer-[^/]+/[0-9][^/]+/images/.*)$ +# Whitelist for expiration, file types not to be removed even when being +# unreferenced. Default: many parts from VfilePattern where no parent index +# exists or might be unknown. +# WfilePattern = (^|.*?/)(Release|InRelease|Release\.gpg|(Packages|Sources)(\.gz|\.bz2|\.lzma|\.xz)?|Translation[^/]*(\.gz|\.bz2|\.lzma|\.xz)?|MD5SUMS|SHA1SUMS|.*\.xml|.*\.db\.tar\.gz|.*\.files\.tar\.gz|.*\.abs\.tar\.gz|[a-z]+32.exe)$|/dists/.*/installer-.*/images/.* + +# Higher modes only working with the debug version +# Warning, writes a lot into apt-cacher.err logfile +# Value overwrites UnbufferLogs setting (aliased) +# Debug:3 + +# Usually, general purpose proxies like Squid expose the IP address of the +# client user to the remote server using the X-Forwarded-For HTTP header. This +# behaviour can be optionally turned on with the Expose-Origin option. +# ExposeOrigin: 0 + +# When logging the originating IP address, trust the information supplied by +# the client in the X-Forwarded-For header. +# LogSubmittedOrigin: 0 + +# The version string reported to the peer, to be displayed as HTTP client (and +# version) in the logs of the mirror. +# WARNING: some archives use this header to detect/guess capabilities of the +# client (i.e. redirection support) and change the behaviour accordingly, while +# ACNG might not support the expected features. Expect side effects. +# +# UserAgent: Yet Another HTTP Client/1.2.3p4 + +# In some cases the Import and Expiration tasks might create fresh volatile +# data for internal use by reconstructing them using patch files. This +# by-product might be recompressed with bzip2 and with some luck the resulting +# file becomes identical to the *.bz2 file on the server, usable for APT +# clients trying to fetch the full .bz2 compressed version. Injection of the +# generated files into the cache has however a disadvantage on underpowered +# servers: bzip2 compression can create high load on the server system and the +# visible download of the busy .bz2 files also becomes slower. +# +# RecompBz2: 0 + +# Network timeout for outgoing connections. +# NetworkTimeout: 60 + +# Sometimes it makes sense to not store the data in cache and just return the +# package data to client as it comes in. DontCache parameters can enable this +# behaviour for certain URL types. The tokens are extended regular expressions +# that URLs are matched against. +# +# DontCacheRequested is applied to the URL as it comes in from the client. +# Example: exclude packages built with kernel-package for x86 +# DontCacheRequested: linux-.*_10\...\.Custo._i386 +# Example usecase: exclude popular private IP ranges from caching +# DontCacheRequested: 192.168.0 ^10\..* 172.30 +# +# DontCacheResolved is applied to URLs after mapping to the target server. If +# multiple backend servers are specified then it's only matched against the +# download link for the FIRST possible source (due to implementation limits). +# Example usecase: all Ubuntu stuff comes from a local mirror (specified as +# backend), don't cache it again: +# DontCacheResolved: ubuntumirror.local.net +# +# DontCache directive sets (overrides) both, DontCacheResolved and +# DontCacheRequested. Provided for convenience, see those directives for +# details. +# +# Default permission set of freshly created files and directories, as octal +# numbers (see chmod(1) for details). +# Can by limited by the umask value (see umask(2) for details) if it's set in +# the environment of the starting shell, e.g. in apt-cacher-ng init script or +# in its configuration file. +# DirPerms: 00755 +# FilePerms: 00664 +# +# +# It's possible to use use apt-cacher-ng as a regular web server with limited +# feature set, i.e. +# including directory browsing and download of any file; +# excluding sorting, mime types/encodings, CGI execution, index page +# redirection and other funny things. +# To get this behavior, mappings between virtual directories and real +# directories on the server must be defined with the LocalDirs directive. +# Virtual and real dirs are separated by spaces, multiple pairs are separated +# by semi-colons. Real directories must be absolute paths. +# NOTE: Since the names of that key directories share the same namespace as +# repository names (see Remap-...) it's administrators job to avoid such +# collisions on them (unless created deliberately). +# +# LocalDirs: woo /data/debarchive/woody ; hamm /data/debarchive/hamm + +# Precache a set of files referenced by specified index files. This can be used +# to create a partial mirror usable for offline work. There are certain limits +# and restrictions on the path specification, see manual for details. A list of +# (maybe) relevant index files could be retrieved via +# "apt-get --print-uris update" on a client machine. +# +# PrecacheFor: debrep/dists/unstable/*/source/Sources* debrep/dists/unstable/*/binary-amd64/Packages* + +# Arbitrary set of data to append to request headers sent over the wire. Should +# be a well formated HTTP headers part including newlines (DOS style) which +# can be entered as escape sequences (\r\n). +# RequestAppendix: X-Tracking-Choice: do-not-track\r\n + +# Specifies the IP protocol families to use for remote connections. Order does +# matter, first specified are considered first. Possible combinations: +# v6 v4 +# v4 v6 +# v6 +# v4 +# (empty or not set: use system default) +# +# ConnectProto: v6 v4 + +# Regular expiration algorithm finds package files which are no longer listed +# in any index file and removes them of them after a safety period. +# This option allows to keep more versions of a package in the cache after +# safety period is over. +# KeepExtraVersions: 1 + +# Optionally uses TCP access control provided by libwrap, see hosts_access(5) +# for details. Daemon name is apt-cacher-ng. Default if not set: decided on +# startup by looking for explicit mentioning of apt-cacher-ng in +# /etc/hosts.allow or /etc/hosts.deny files. +# UseWrap: 0 + +# If many machines from the same local network attempt to update index files +# (apt-get update) at nearly the same time, the known state of these index file +# is temporarily frozen and multiple requests receive the cached response +# without contacting the server. This parameter (in seconds) specifies the +# length of this period before the files are considered outdated. +# Setting it too low transfers more data and increases remote server load, +# setting it too high (more than a couple of minutes) increases the risk of +# delivering inconsistent responses to the clients. +# FreshIndexMaxAge: 27 + +# Usually the users are not allowed to specify custom TCP ports of remote +# mirrors in the requests, only the default HTTP port can be used (instead, +# proxy administrator can create Remap- rules with custom ports). This +# restriction can be disabled by specifying a list of allowed ports or 0 for +# any port. +# +# AllowUserPorts: 80 + +# Normally the HTTP redirection responses are forwarded to the original caller +# (i.e. APT) which starts a new download attempt from the new URL. This +# solution is ok for client configurations with proxy mode but doesn't work +# well with configurations using URL prefixes. To work around this the server +# can restart its own download with another URL. However, this might be used to +# circumvent download source policies by malicious users. +# The RedirMax option specifies how many such redirects the server should +# follow per request, 0 disables the internal redirection. If not set, +# default value is 0 if ForceManaged is used and 5 otherwise. +# +# RedirMax: 5 diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/README.md b/chef/cookbooks/apt/test/cookbooks/apt_test/README.md new file mode 100644 index 0000000..6e1f578 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/README.md @@ -0,0 +1 @@ +This cookbook is used with test-kitchen to test the parent, apt cookbok diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb new file mode 100644 index 0000000..db617a2 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-client_test.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: apt_test +# Recipe:: cacher-client_test +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apt_test::cacher-client" do + include Helpers::AptTest + + it 'does not create 01proxy' do + file('/etc/apt/apt.conf.d/01proxy').wont_exist + end + +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb new file mode 100644 index 0000000..75532cc --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng-client_test.rb @@ -0,0 +1,41 @@ +# +# Cookbook Name:: apt_test +# Recipe:: cacher-ng-client_test.rb +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apt_test::cacher-ng-client" do + include Helpers::AptTest + + it 'creates the cacher_dir' do + directory(node['apt']['cacher_dir']).must_exist.with(:owner, "apt-cacher-ng") + end + + it 'runs the cacher service' do + service("apt-cacher-ng").must_be_running + end + + it 'creates 01proxy' do + file('/etc/apt/apt.conf.d/01proxy').must_include "Acquire::http::Proxy \"http://#{node['ipaddress']}:#{node['apt']['cacher_port']}\";" + end + + it 'installed colordiff' do + package('colordiff').must_be_installed + end + +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb new file mode 100644 index 0000000..f3cdce5 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/cacher-ng_test.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: apt_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apt_test::default" do + include Helpers::AptTest + + it 'runs the cacher service' do + service("apt-cacher-ng").must_be_running + end +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..ab97676 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/default_test.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: apt_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apt_test::default" do + include Helpers::AptTest + + it 'creates the preseeding directory' do + directory('/var/cache/local/preseeding').must_exist + end +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb new file mode 100644 index 0000000..b9c5cb3 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/lwrps_test.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: apt_test +# Recipe:: lwrps +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "apt_test::lwrps" do + include Helpers::AptTest + + it 'creates the Opscode sources.list' do + file("/etc/apt/sources.list.d/opscode.list").must_exist + end + + it 'adds the Opscode package signing key' do + opscode_key = shell_out("apt-key list") + assert opscode_key.stdout.include?("Opscode Packages ") + end + + it 'creates the correct pinning preferences for chef' do + pinning_prefs = "Package: chef\nPin: version 10.16.2-1" + file("/etc/apt/preferences.d/chef").must_match(/#{pinning_prefs}/) + end + + it 'creates a repo with an architecture' do + cloudera = "deb\s+\\[arch=amd64\\] http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh precise-cdh4 contrib" + file("/etc/apt/sources.list.d/cloudera.list").must_match(/#{cloudera}/) + end + + it 'creates the correct pinning preferences with a glob' do + pinning_prefs = "Package: \\*\nPin: origin packages.dotdeb.org" + file("/etc/apt/preferences.d/dotdeb").must_match(/#{pinning_prefs}/) + end +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..33a4ac8 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: apt_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Helpers + module AptTest + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + end +end diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb new file mode 100644 index 0000000..f909851 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/metadata.rb @@ -0,0 +1,6 @@ +name "apt_test" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "This cookbook is used with test-kitchen to test the parent, apt cookbok" +version "1.0.0" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb new file mode 100644 index 0000000..432db27 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-client.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apt_test +# Recipe:: cacher-client +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt::cacher-client" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb new file mode 100644 index 0000000..dab7cea --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng-client.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: apt_test +# Recipe:: cacher-ng-client +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt::cacher-ng" +include_recipe "apt::cacher-client" + +#install a small, innocuous application to verify this works +package "colordiff" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb new file mode 100644 index 0000000..20ae214 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/cacher-ng.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apt_test +# Recipe:: cacher-ng +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt::cacher-ng" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb new file mode 100644 index 0000000..ce4d3f9 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apt_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt::default" diff --git a/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb new file mode 100644 index 0000000..a0a3a38 --- /dev/null +++ b/chef/cookbooks/apt/test/cookbooks/apt_test/recipes/lwrps.rb @@ -0,0 +1,66 @@ +# +# Cookbook Name:: apt_test +# Recipe:: lwrps +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt" + +# Apt Repository +apt_repository "opscode" do + uri "http://apt.opscode.com" + components ["main"] + distribution "#{node['lsb']['codename']}-0.10" + key "2940ABA983EF826A" + keyserver "pgpkeys.mit.edu" + action :add +end + +# Apt Repository with arch +apt_repository "cloudera" do + uri "http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh" + arch "amd64" + distribution "precise-cdh4" + components ["contrib"] + key "http://archive.cloudera.com/debian/archive.key" + action :add +end + +# Apt repository and install a package it contains +apt_repository "nginx" do + uri "http://nginx.org/packages/#{node['platform']}" + distribution node['lsb']['codename'] + components ["nginx"] + key "http://nginx.org/keys/nginx_signing.key" + deb_src true +end + +package "nginx-debug" do + action :upgrade +end + +# Apt Preferences +apt_preference "chef" do + pin "version 10.16.2-1" + pin_priority "700" +end + +# COOK-2338 +apt_preference "dotdeb" do + glob "*" + pin "origin packages.dotdeb.org " + pin_priority "700" +end diff --git a/chef/cookbooks/aws/CHANGELOG.md b/chef/cookbooks/aws/CHANGELOG.md new file mode 100644 index 0000000..84f49ea --- /dev/null +++ b/chef/cookbooks/aws/CHANGELOG.md @@ -0,0 +1,71 @@ +aws Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the aws cookbook. + + +v0.101.4 +-------- +### Improvement +- **[COOK-3345](https://tickets.opscode.com/browse/COOK-3345)** - Add `aws_s3_file` LWRP +- **[COOK-3264](https://tickets.opscode.com/browse/COOK-3264)** - Allow specifying of file ownership for `ebs_raid` resource `mount_point` + +### Bug +- **[COOK-3308](https://tickets.opscode.com/browse/COOK-3308)** - Ensure mdadm properly allocates the device number + +v0.101.2 +-------- +### Bug + +- [COOK-2951]: aws cookbook has foodcritic failures + +### Improvement + +- [COOK-1471]: aws cookbook should mention the route53 cookbook + +v0.101.0 +-------- +### Bug + +- [COOK-1355]: AWS::ElasticIP recipe uses an old RightAWS API to associate an elastic ip address to an EC2 instance +- [COOK-2659]: `volume_compatible_with_resource_definition` fails on valid `snapshot_id` configurations +- [COOK-2670]: AWS cookbook doesn't use `node[:aws][:databag_name]`, etc. in `create_raid_disks` +- [COOK-2693]: exclude AWS reserved tags from tag update +- [COOK-2914]: Foodcritic failures in Cookbooks + +### Improvement + +- [COOK-2587]: Resource attribute for using most recent snapshot instead of earliest +- [COOK-2605]: "WARN: Missing gem '`right_aws`'" always prints when including 'aws' in metadata + +### New Feature + +- [COOK-2503]: add EBS raid volumes and provisioned IOPS support for AWS + +v0.100.6 +-------- +- [COOK-2148] - `aws_ebs_volume` attach action saves nil `volume_id` in node + +v0.100.4 +-------- +- Support why-run mode in LWRPs +- [COOK-1836] - make `aws_elastic_lb` idempotent + +v0.100.2 +-------- +- [COOK-1568] - switch to chef_gem resource +- [COOK-1426] - declare default actions for LWRPs + +v0.100.0 +-------- +- [COOK-1221] - convert node attribute accessors to strings +- [COOK-1195] - manipulate AWS resource tags (instances, volumes, snapshots +- [COOK-627] - add aws_elb (elastic load balancer) LWRP + +v0.99.1 +------- +- [COOK-530] - aws cookbook doesn't save attributes with chef 0.10.RC.0 +- [COOK-600] - In AWS Cookbook specifying just the device doesn't work +- [COOK-601] - in aws cookbook :prune action keeps 1 less snapshot than snapshots_to_keep +- [COOK-610] - Create Snapshot action in aws cookbook should allow description attribute +- [COOK-819] - fix documentation bug in aws readme +- [COOK-829] - AWS cookbook does not work with most recent right_aws gem but no version is locked in the recipe diff --git a/chef/cookbooks/aws/CONTRIBUTING b/chef/cookbooks/aws/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/aws/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/aws/LICENSE b/chef/cookbooks/aws/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/aws/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/aws/README.md b/chef/cookbooks/aws/README.md new file mode 100644 index 0000000..f3ee250 --- /dev/null +++ b/chef/cookbooks/aws/README.md @@ -0,0 +1,355 @@ +Description +=========== + +This cookbook provides libraries, resources and providers to configure +and manage Amazon Web Services components and offerings with the EC2 +API. Currently supported resources: + +* EBS Volumes (`ebs_volume`) +* EBS Raid (`ebs_raid`) +* Elastic IPs (`elastic_ip`) +* Elastic Load Balancer (`elastic_lb`) +* AWS Resource Tags (`resource_tag`) + +Unsupported AWS resources that have other cookbooks include but are +not limited to: + +* [Route53](http://community.opscode.com/cookbooks/route53) + +**Note** This cookbook uses the `right_aws` RubyGem to interact with + the AWS API because at the time it was written, `fog` and `aws-sdk` + were not available. Further, both of those gems require `nokogiri` + which requires compiling native extensions, which means build tools + are required. We do not plan at this time to change the underlying + Ruby library used in order to limit the external dependencies for + this cookbook. + +Requirements +============ + +Requires Chef 0.7.10 or higher for Lightweight Resource and Provider +support. Chef 0.8+ is recommended. While this cookbook can be used in +`chef-solo` mode, to gain the most flexibility, we recommend using +`chef-client` with a Chef Server. + +An Amazon Web Services account is required. The Access Key and Secret +Access Key are used to authenticate with EC2. + +AWS Credentials +=============== + +In order to manage AWS components, authentication credentials need to +be available to the node. There are a number of ways to handle this, +such as node attributes or roles. We recommend storing these in a +databag (Chef 0.8+), and loading them in the recipe where the +resources are needed. + +DataBag recommendation: + + % knife data bag show aws main + { + "id": "main", + "aws_access_key_id": "YOUR_ACCESS_KEY", + "aws_secret_access_key": "YOUR_SECRET_ACCESS_KEY" + } + +This can be loaded in a recipe with: + + aws = data_bag_item("aws", "main") + +And to access the values: + + aws['aws_access_key_id'] + aws['aws_secret_access_key'] + +We'll look at specific usage below. + +Recipes +======= + +default.rb +---------- + +The default recipe installs the `right_aws` RubyGem, which this +cookbook requires in order to work with the EC2 API. Make sure that +the aws recipe is in the node or role `run_list` before any resources +from this cookbook are used. + + "run_list": [ + "recipe[aws]" + ] + +The `gem_package` is created as a Ruby Object and thus installed +during the Compile Phase of the Chef run. + +Libraries +========= + +The cookbook has a library module, `Opscode::AWS::Ec2`, which can be +included where necessary: + + include Opscode::Aws::Ec2 + +This is needed in any providers in the cookbook. Along with some +helper methods used in the providers, it sets up a class variable, +`ec2` that is used along with the access and secret access keys + +Resources and Providers +======================= + +This cookbook provides two resources and corresponding providers. + +## ebs_volume.rb + + +Manage Elastic Block Store (EBS) volumes with this resource. + +Actions: + +* `create` - create a new volume. +* `attach` - attach the specified volume. +* `detach` - detach the specified volume. +* `snapshot` - create a snapshot of the volume. +* `prune` - prune snapshots. + +Attribute Parameters: + +* `aws_secret_access_key`, `aws_access_key` - passed to + `Opscode::AWS:Ec2` to authenticate, required. +* `size` - size of the volume in gigabytes. +* `snapshot_id` - snapshot to build EBS volume from. +* most_recent_snapshot - use the most recent snapshot when creating a + volume from an existing volume (defaults to false) +* `availability_zone` - EC2 region, and is normally automatically + detected. +* `device` - local block device to attach the volume to, e.g. + `/dev/sdi` but no default value, required. +* `volume_id` - specify an ID to attach, cannot be used with action + `:create` because AWS assigns new volume IDs +* `timeout` - connection timeout for EC2 API. +* `snapshots_to_keep` - used with action `:prune` for number of + snapshots to maintain. +* `description` - used to set the description of an EBS snapshot +* `volume_type` - "standard" or "io1" (io1 is the type for IOPS volume) +* `piops` - number of Provisioned IOPS to provision, must be > 100 + +## ebs_raid.rb + +Manage Elastic Block Store (EBS) raid devices with this resource. + +Attribute Parameters: + +* `mount_point` - where to mount the RAID volume +* `mount_point_owner` - the owner of the mount point (default root) +* `mount_point_group` - the group of the mount point (default root) +* `mount_point_mode` - the file mode of the mount point (default 0755) +* `disk_count` - number of EBS volumes to raid +* `disk_size` - size of EBS volumes to raid +* `level` - RAID level (default 10) +* `filesystem` - filesystem to format raid array (default ext4) +* `snapshots` - array of EBS snapshots to restore. Snapshots must be + taken using an ec2 consistent snapshot tool, and tagged with a + number that indicates how many devices are in the array being backed + up (e.g. "Logs Backup [0-4]" for a four-volume raid array snapshot) +* `disk_type` - "standard" or "io1" (io1 is the type for IOPS volume) +* `disk_piops` - number of Provisioned IOPS to provision per disk, + must be > 100 + +## elastic_ip.rb + +Actions: + +* `associate` - associate the IP. +* `disassociate` - disassociate the IP. + +Attribute Parameters: + +* `aws_secret_access_key`, `aws_access_key` - passed to + `Opscode::AWS:Ec2` to authenticate, required. +* `ip` - the IP address. +* `timeout` - connection timeout for EC2 API. + +## elastic_lb.rb + +Actions: + +* `register` - Add this instance to the LB +* `deregister` - Remove this instance from the LB + +Attribute Parameters: + +* `aws_secret_access_key`, `aws_access_key` - passed to + `Opscode::AWS:Ec2` to authenticate, required. +* `name` - the name of the LB, required. + +## resource_tag.rb + +Actions: + +* `add` - Add tags to a resource. +* `update` - Add or modify existing tags on a resource -- this is the + default action. +* `remove` - Remove tags from a resource, but only if the specified + values match the existing ones. +* `force_remove` - Remove tags from a resource, regardless of their + values. + +Attribute Parameters + +* `aws_secret_access_key`, `aws_access_key` - passed to + `Opscode::AWS:Ec2` to authenticate, required. +* `tags` - a hash of key value pairs to be used as resource tags, + (e.g. `{ "Name" => "foo", "Environment" => node.chef_environment + }`,) required. +* `resource_id` - resources whose tags will be modified. The value may + be a single ID as a string or multiple IDs in an array. If no + `resource_id` is specified the name attribute will be used. + +Usage +===== + +The following examples assume that the recommended data bag item has +been created and that the following has been included at the top of +the recipe where they are used. + + include_recipe "aws" + aws = data_bag_item("aws", "main") + +## aws_ebs_volume + +The resource only handles manipulating the EBS volume, additional +resources need to be created in the recipe to manage the attached +volume as a filesystem or logical volume. + + aws_ebs_volume "db_ebs_volume" do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + size 50 + device "/dev/sdi" + action [ :create, :attach ] + end + +This will create a 50G volume, attach it to the instance as `/dev/sdi`. + + aws_ebs_volume "db_ebs_volume_from_snapshot" do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + size 50 + device "/dev/sdi" + snapshot_id "snap-ABCDEFGH" + action [ :create, :attach ] + end + +This will create a new 50G volume from the snapshot ID provided and +attach it as `/dev/sdi`. + +## aws_elastic_ip + +The `elastic_ip` resource provider does not support allocating new +IPs. This must be done before running a recipe that uses the resource. +After allocating a new Elastic IP, we recommend storing it in a +databag and loading the item in the recipe. + +Databag structure: + + % knife data bag show aws eip_load_balancer_production + { + "id": "eip_load_balancer_production", + "public_ip": "YOUR_ALLOCATED_IP" + } + +Then to set up the Elastic IP on a system: + + ip_info = data_bag_item("aws", "eip_load_balancer_production") + + aws_elastic_ip "eip_load_balancer_production" do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + ip ip_info['public_ip'] + action :associate + end + +This will use the loaded `aws` and `ip_info` databags to pass the +required values into the resource to configure. Note that when +associating an Elastic IP to an instance, connectivity to the instance +will be lost because the public IP address is changed. You will need +to reconnect to the instance with the new IP. + +You can also store this in a role as an attribute or assign to the +node directly, if preferred. + +## aws_elastic_lb + +`elastic_lb` opererates similar to `elastic_ip'. Make sure that you've +created the ELB and enabled your instances' availability zones prior +to using this provider. + +For example, to register the node in the 'QA' ELB: + + aws_elastic_lb "elb_qa" do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + name "QA" + action :register + end + +## aws_resource_tag + +`resource_tag` can be used to manipulate the tags assigned to one or +more AWS resources, i.e. ec2 instances, ebs volumes or ebs volume +snapshots. + +Assigining tags to a node to reflect it's role and environment: + + aws_resource_tag node['ec2']['instance_id'] do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + tags({"Name" => "www.example.com app server", + "Environment" => node.chef_environment}) + action :update + end + +Assigning a set of tags to multiple resources, e.g. ebs volumes in a +disk set: + + aws_resource_tag 'my awesome raid set' do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + resource_id [ "vol-d0518cb2", "vol-fad31a9a", "vol-fb106a9f", "vol-74ed3b14" ] + tags({"Name" => "My awesome RAID disk set", + "Environment" => node.chef_environment}) + end + +## aws_s3_file + +`s3_file` can be used to download a file from s3 that requires aws authorization. This +is a wrapper around `remote_file` and supports the same resource attributes as `remote_file`. + + aws_s3_file "/tmp/foo" do + bucket "i_haz_an_s3_buckit" + remote_path "path/in/s3/bukket/to/foo" + aws_access_key_id aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + end + + +License and Author +================== + +* Author:: Chris Walters () +* Author:: AJ Christensen () +* Author:: Justin Huff () + +Copyright 2009-2013, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/aws/attributes/default.rb b/chef/cookbooks/aws/attributes/default.rb new file mode 100644 index 0000000..eb65095 --- /dev/null +++ b/chef/cookbooks/aws/attributes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: aws +# Attributes:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['aws']['right_aws_version'] = "3.0.5" +default['aws']['databag_name'] = "aws" +default['aws']['databag_entry'] = "main" diff --git a/chef/cookbooks/aws/libraries/ec2.rb b/chef/cookbooks/aws/libraries/ec2.rb new file mode 100644 index 0000000..23de193 --- /dev/null +++ b/chef/cookbooks/aws/libraries/ec2.rb @@ -0,0 +1,80 @@ +# +# Copyright:: Copyright (c) 2009 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# TODO: once sync_libraries properly handles sub-directories, move this file to aws/libraries/opscode/aws/ec2.rb + +require 'open-uri' + +module Opscode + module Aws + module Ec2 + def find_snapshot_id(volume_id="", find_most_recent=false) + snapshot_id = nil + snapshots = if find_most_recent + ec2.describe_snapshots.sort { |a,b| a[:aws_started_at] <=> b[:aws_started_at] } + else + ec2.describe_snapshots.sort { |a,b| b[:aws_started_at] <=> a[:aws_started_at] } + end + snapshots.each do |snapshot| + if snapshot[:aws_volume_id] == volume_id + snapshot_id = snapshot[:aws_id] + end + end + raise "Cannot find snapshot id!" unless snapshot_id + Chef::Log.debug("Snapshot ID is #{snapshot_id}") + snapshot_id + end + + def ec2 + begin + require 'right_aws' + rescue LoadError + Chef::Log.error("Missing gem 'right_aws'. Use the default aws recipe to install it first.") + end + + region = instance_availability_zone + region = region[0, region.length-1] + @@ec2 ||= RightAws::Ec2.new(new_resource.aws_access_key, new_resource.aws_secret_access_key, { :logger => Chef::Log, :region => region }) + end + + def instance_id + @@instance_id ||= query_instance_id + end + + def instance_availability_zone + @@instance_availability_zone ||= query_instance_availability_zone + end + + private + + def query_instance_id + instance_id = open('http://169.254.169.254/latest/meta-data/instance-id'){|f| f.gets} + raise "Cannot find instance id!" unless instance_id + Chef::Log.debug("Instance ID is #{instance_id}") + instance_id + end + + def query_instance_availability_zone + availability_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone/'){|f| f.gets} + raise "Cannot find availability zone!" unless availability_zone + Chef::Log.debug("Instance's availability zone is #{availability_zone}") + availability_zone + end + + end + end +end diff --git a/chef/cookbooks/aws/metadata.rb b/chef/cookbooks/aws/metadata.rb new file mode 100644 index 0000000..1ab7ecd --- /dev/null +++ b/chef/cookbooks/aws/metadata.rb @@ -0,0 +1,8 @@ +name "aws" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "LWRPs for managing AWS resources" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.101.4" +recipe "aws", "Installs the right_aws gem during compile time" diff --git a/chef/cookbooks/aws/providers/ebs_raid.rb b/chef/cookbooks/aws/providers/ebs_raid.rb new file mode 100644 index 0000000..dba3900 --- /dev/null +++ b/chef/cookbooks/aws/providers/ebs_raid.rb @@ -0,0 +1,435 @@ +include Opscode::Aws::Ec2 + +action :auto_attach do + + package "mdadm" do + action :install + end + + # Baseline expectations. + node.set['aws'] ||= {} + node.set[:aws][:raid] ||= {} + + # Mount point information. + node.set[:aws][:raid][@new_resource.mount_point] ||= {} + + # we're done we successfully located what we needed + if !already_mounted(@new_resource.mount_point) && !locate_and_mount(@new_resource.mount_point, @new_resource.mount_point_owner, + @new_resource.mount_point_group, @new_resource.mount_point_mode, + @new_resource.filesystem, @new_resource.filesystem_options) + + # If we get here, we couldn't auto attach, nor re-allocate an existing set of disks to ourselves. Auto create the md devices + + # Stopping udev to ensure RAID md device allocates md0 properly + manage_udev("stop") + + create_raid_disks(@new_resource.mount_point, + @new_resource.mount_point_owner, + @new_resource.mount_point_group, + @new_resource.mount_point_mode, + @new_resource.disk_count, + @new_resource.disk_size, + @new_resource.level, + @new_resource.filesystem, + @new_resource.filesystem_options, + @new_resource.snapshots, + @new_resource.disk_type, + @new_resource.disk_piops) + + @new_resource.updated_by_last_action(true) + end +end + +private + +# AWS's volume attachment interface assumes that we're using +# sdX style device names. The ones we actually get will be xvdX +def find_free_volume_device_prefix + # Specific to ubuntu 11./12. + vol_dev = "sdh" + + begin + vol_dev = vol_dev.next + base_device = "/dev/#{vol_dev}1" + Chef::Log.info("dev pre trim #{base_device}") + end while ::File.exists?(base_device) + + vol_dev +end + +def find_free_md_device_name + number=0 + #TODO, this won't work with more than 10 md devices + begin + dir = "/dev/md#{number}" + Chef::Log.info("md pre trim #{dir}") + number +=1 + end while ::File.exists?(dir) + + dir[5, dir.length] +end + +def md_device_from_mount_point(mount_point) + md_device = "" + Dir.glob("/dev/md[0-9]*").each do |dir| + # Look at the mount point directory and see if containing device + # is the same as the md device. + if ::File.lstat(dir).rdev == ::File.lstat(mount_point).dev + md_device = dir + break + end + end + md_device +end + +def update_node_from_md_device(md_device, mount_point) + command = "mdadm --misc -D #{md_device} | grep '/dev/s\\|/xv' | awk '{print $7}' | tr '\\n' ' '" + Chef::Log.info("Running #{command}") + raid_devices = `#{command}` + Chef::Log.info("already found the mounted device, created from #{raid_devices}") + + node.set[:aws][:raid][mount_point][:raid_dev] = md_device.sub(/\/dev\//,"") + node.set[:aws][:raid][mount_point][:devices] = raid_devices + node.save +end + +# Dumb way to look for mounted raid devices. Assumes that the machine +# will only create one. +def find_md_device + md_device = nil + Dir.glob("/dev/md[0-9]*").each do |dir| + Chef::Log.error("More than one /dev/mdX found.") unless md_device.nil? + md_device = dir + end + md_device +end + +def already_mounted(mount_point) + if !::File.exists?(mount_point) + return false + end + + md_device = md_device_from_mount_point(mount_point) + if !md_device || md_device == "" + return false + end + + update_node_from_md_device(md_device, mount_point) + + return true +end + +private +def udev(cmd, log) + execute log do + Chef::Log.debug(log) + command "udevadm control #{cmd}" + end +end + +def update_initramfs() + execute "updating initramfs" do + Chef::Log.debug("updating initramfs to ensure RAID config persists reboots") + command "update-initramfs -u" + end +end + +def manage_udev(action) + if action == "stop" + udev("--stop-exec-queue", "stopping udev...") + elsif action == "start" + udev("--start-exec-queue", "starting udev queued events..") + else + Chef::Log.fatal("Incorrect action passed to manage_udev") + end +end + +# Attempt to find an unused data bag and mount all the EBS volumes to our system +# Note: recovery from this assumed state is weakly untested. +def locate_and_mount(mount_point, mount_point_owner, mount_point_group, mount_point_mode, filesystem, filesystem_options) + + if node['aws'].nil? || node['aws']['raid'].nil? || node['aws']['raid'][mount_point].nil? + Chef::Log.info("No mount point found '#{mount_point}' for node") + return false + end + + if node['aws']['raid'][mount_point]['raid_dev'].nil? || node['aws']['raid'][mount_point]['device_map'].nil? + Chef::Log.info("No raid device found for mount point '#{mount_point}' for node") + return false + end + + raid_dev = node['aws']['raid'][mount_point]['raid_dev'] + devices_string = device_map_to_string(node['aws']['raid'][mount_point]['device_map']) + + Chef::Log.info("Raid device is #{raid_dev} and mount path is #{mount_point}") + + # Stop udev + manage_udev("stop") + + # Mount volumes + mount_volumes(node['aws']['raid'][mount_point]['device_map']) + + # Assemble raid device. + assemble_raid(raid_dev, devices_string) + + # Now mount the drive + mount_device(raid_dev, mount_point, mount_point_owner, mount_point_group, mount_point_mode, filesystem, filesystem_options) + + # update initramfs to ensure RAID config persists reboots + update_initramfs() + + # Start udev back up + manage_udev("start") + + true +end + +# TODO fix this kludge: ideally we'd pull in the device information from the ebs_volume +# resource but it's not up-to-date at this time without breaking this action up. +def correct_device_map(device_map) + corrected_device_map = {} + # Rekey + device_map.keys.each do |k| + if k.start_with?('sd') + new_k = 'xvd' + k[2..-1] + if corrected_device_map.include?(new_k) + Chef::Log.error("Unable to remap due to collision.") + return {} + end + corrected_device_map[new_k] = device_map[k] + else + corrected_device_map[k] = device_map[k] + end + end + corrected_device_map +end + +# Generate the string using the corrected map. +def device_map_to_string(device_map) + corrected_map = correct_device_map(device_map) + + devices_string = "" + corrected_map.keys.sort.each do |k| + devices_string += "/dev/#{k} " + end + devices_string +end + +def mount_volumes(device_vol_map) + # Attach the volumes + device_vol_map.keys.sort.each do |dev_device| + attach_volume(dev_device, device_vol_map[dev_device]) + end + + # Wait until all volumes are mounted + ruby_block "wait_#{new_resource.name}" do + block do + count = 0 + begin + Chef::Log.info("sleeping 10 seconds until EBS volumes have re-attached") + sleep 10 + count += 1 + end while !device_vol_map.all? {|dev_path| ::File.exists?(dev_path) } + + # Accounting to see how often this code actually gets used. + node.set[:aws][:raid][mount_point][:device_attach_delay] = count * 10 + end + end +end + +# Assembles the raid if it doesn't already exist +# Note: raid_dev is the "suggested" location. mdadm may actually put it somewhere else. +def assemble_raid(raid_dev, devices_string) + if ::File.exists?(raid_dev) + Chef::Log.info("Device #{raid_dev} exists skipping") + return + end + + Chef::Log.info("Raid device #{raid_dev} does not exist re-assembling") + Chef::Log.debug("Devices for #{raid_dev} are #{devices_string}") + + # Now that attach is done we re-build the md device + # We have to grab the UUID of the md device or the disks will be assembled with the UUID stored + # within the superblock metadata, causing the md_device number to be randomly + # chosen if restore is happening on a different host + execute "re-attaching raid device" do + command "mdadm -A --uuid=`mdadm -E --scan|awk '{print $4}'|sed 's/UUID=//g'` #{raid_dev} #{devices_string}" + # mdadm may return 2 but still return a clean raid device. + returns [0, 2] + end +end + +def mount_device(raid_dev, mount_point, mount_point_owner, mount_point_group, mount_point_mode, filesystem, filesystem_options) + # Create the mount point + directory mount_point do + owner mount_point_owner + group mount_point_group + mode mount_point_mode + action :create + not_if "test -d #{mount_point}" + end + + # Try to figure out the actual device. + ruby_block "find md device in #{new_resource.name}" do + block do + if ::File.exists?(mount_point) + Chef::Log.info("Already mounted: #{mount_point}") + end + + # For some silly reason we can't call the function. + md_device = nil + Dir.glob("/dev/md[0-9]*").each do |dir| + Chef::Log.error("More than one /dev/mdX found.") unless md_device.nil? + md_device = dir + end + + Chef::Log.info("Found #{md_device}") + + # the mountpoint must be determined dynamically, so I can't use the chef mount + system("mount -t #{filesystem} -o #{filesystem_options} #{md_device} #{mount_point}") + end + end +end + +# Attach all existing ami instances if they exist on this node, if not, we want an error to occur Detects disk from node information +def attach_volume(disk_dev, volume_id) + disk_dev_path = "/dev/#{disk_dev}" + + aws = data_bag_item(node['aws']['databag_name'], node['aws']['databag_entry']) + + Chef::Log.info("Attaching existing ebs volume id #{volume_id} for device #{disk_dev_path}") + + aws_ebs_volume disk_dev_path do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + device disk_dev_path + name disk_dev + volume_id volume_id + action [:attach] + provider "aws_ebs_volume" + end +end + +# Mount point for where to mount I.E /mnt/filesystem +# Diskset I.E sdi (which creates sdi1-sdi +# Raid size. The total size of the array +# Raid level. The raid level to use. +# Filesystem. The file system to create. +# Filesystem_options The options to pass to mount +# Snapshots. The list of snapshots to create the ebs volumes from. +# If it's not nil, must have exactly elements + +def create_raid_disks(mount_point, mount_point_owner, mount_point_group, mount_point_mode, num_disks, disk_size, + level, filesystem, filesystem_options, snapshots, disk_type, disk_piops) + + creating_from_snapshot = !(snapshots.nil? || snapshots.size == 0) + + disk_dev = find_free_volume_device_prefix + Chef::Log.debug("vol device prefix is #{disk_dev}") + + raid_dev = find_free_md_device_name + Chef::Log.debug("target raid device is #{raid_dev}") + + devices = {} + + # For each volume add information to the mount metadata + (1..num_disks).each do |i| + + disk_dev_path = "#{disk_dev}#{i}" + + aws = data_bag_item(node['aws']['databag_name'], node['aws']['databag_entry']) + + Chef::Log.info "Snapshot array is #{snapshots[i-1]}" + aws_ebs_volume disk_dev_path do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + size disk_size + volume_type disk_type + piops disk_piops + device "/dev/#{disk_dev_path}" + name disk_dev_path + action [:create, :attach] + snapshot_id creating_from_snapshot ? snapshots[i-1] : "" + provider "aws_ebs_volume" + + # set up our data bag info + devices[disk_dev_path] = "pending" + + Chef::Log.info("creating ebs volume for device #{disk_dev_path} with size #{disk_size}") + end + + Chef::Log.info("attach dev: #{disk_dev_path}") + end + + ruby_block "sleeping_#{new_resource.name}" do + block do + Chef::Log.debug("sleeping 10 seconds to let drives attach") + sleep 10 + end + end + + # Create the raid device strings w/sd => xvd correction + devices_string = device_map_to_string(devices) + Chef::Log.info("finished sorting devices #{devices_string}") + + if not creating_from_snapshot + # Create the raid device on our system + execute "creating raid device" do + Chef::Log.info("creating raid device /dev/#{raid_dev} with raid devices #{devices_string}") + command "mdadm --create /dev/#{raid_dev} --level=#{level} --raid-devices=#{devices.size} #{devices_string}" + end + + # NOTE: must be a better way. + # Try to figure out the actual device. + ruby_block "formatting md device in #{new_resource.name}" do + block do + # For some silly reason we can't call the function. + md_device = nil + Dir.glob("/dev/md[0-9]*").each do |dir| + Chef::Log.error("More than one /dev/mdX found.") unless md_device.nil? + md_device = dir + end + + Chef::Log.info("Format device found: #{md_device}") + case filesystem + when "ext4" + system("mke2fs -t #{filesystem} -F #{md_device}") + else + #TODO fill in details on how to format other filesystems here + Chef::Log.info("Can't format filesystem #{filesystem}") + end + end + end + else + # Reassembling the raid device on our system + assemble_raid("/dev/#{raid_dev}", devices_string) + end + + # start udev + manage_udev("start") + + # Mount the device + mount_device(raid_dev, mount_point, mount_point_owner, mount_point_group, mount_point_mode, filesystem, filesystem_options) + + # update initramfs to ensure RAID config persists reboots + update_initramfs() + + # Not invoked until the volumes have been successfully created and attached + ruby_block "databagupdate" do + block do + Chef::Log.info("finished creating disks") + + devices.each_pair do |key, value| + value = node['aws']['ebs_volume'][key]['volume_id'] + devices[key] = value + Chef::Log.info("value is #{value}") + end + + # Assemble all the data bag meta data + node.set[:aws][:raid][mount_point][:raid_dev] = raid_dev + node.set[:aws][:raid][mount_point][:device_map] = devices + node.save + end + end + +end diff --git a/chef/cookbooks/aws/providers/ebs_volume.rb b/chef/cookbooks/aws/providers/ebs_volume.rb new file mode 100644 index 0000000..18fda93 --- /dev/null +++ b/chef/cookbooks/aws/providers/ebs_volume.rb @@ -0,0 +1,264 @@ +include Opscode::Aws::Ec2 + +# Support whyrun +def whyrun_supported? + true +end + +action :create do + raise "Cannot create a volume with a specific id (EC2 chooses volume ids)" if new_resource.volume_id + if new_resource.snapshot_id =~ /vol/ + new_resource.snapshot_id(find_snapshot_id(new_resource.snapshot_id, new_resource.most_recent_snapshot)) + end + + nvid = volume_id_in_node_data + if nvid + # volume id is registered in the node data, so check that the volume in fact exists in EC2 + vol = volume_by_id(nvid) + exists = vol && vol[:aws_status] != "deleting" + # TODO: determine whether this should be an error or just cause a new volume to be created. Currently erring on the side of failing loudly + raise "Volume with id #{nvid} is registered with the node but does not exist in EC2. To clear this error, remove the ['aws']['ebs_volume']['#{new_resource.name}']['volume_id'] entry from this node's data." unless exists + else + # Determine if there is a volume that meets the resource's specifications and is attached to the current + # instance in case a previous [:create, :attach] run created and attached a volume but for some reason was + # not registered in the node data (e.g. an exception is thrown after the attach_volume request was accepted + # by EC2, causing the node data to not be stored on the server) + if new_resource.device && (attached_volume = currently_attached_volume(instance_id, new_resource.device)) + Chef::Log.debug("There is already a volume attached at device #{new_resource.device}") + compatible = volume_compatible_with_resource_definition?(attached_volume) + raise "Volume #{attached_volume[:aws_id]} attached at #{attached_volume[:aws_device]} but does not conform to this resource's specifications" unless compatible + Chef::Log.debug("The volume matches the resource's definition, so the volume is assumed to be already created") + converge_by("update the node data with volume id: #{attached_volume[:aws_id]}") do + node.set['aws']['ebs_volume'][new_resource.name]['volume_id'] = attached_volume[:aws_id] + node.save unless Chef::Config[:solo] + end + else + # If not, create volume and register its id in the node data + converge_by("create a volume with id=#{new_resource.snapshot_id} size=#{new_resource.size} availability_zone=#{new_resource.availability_zone} and update the node data with created volume's id") do + nvid = create_volume(new_resource.snapshot_id, + new_resource.size, + new_resource.availability_zone, + new_resource.timeout, + new_resource.volume_type, + new_resource.piops) + node.set['aws']['ebs_volume'][new_resource.name]['volume_id'] = nvid + node.save unless Chef::Config[:solo] + end + end + end +end + +action :attach do + # determine_volume returns a Hash, not a Mash, and the keys are + # symbols, not strings. + vol = determine_volume + + if vol[:aws_status] == "in-use" + if vol[:aws_instance_id] != instance_id + raise "Volume with id #{vol[:aws_id]} exists but is attached to instance #{vol[:aws_instance_id]}" + else + Chef::Log.debug("Volume is already attached") + end + else + converge_by("attach the volume with aws_id=#{vol[:aws_id]} id=#{instance_id} device=#{new_resource.device} and update the node data with created volume's id") do + # attach the volume and register its id in the node data + attach_volume(vol[:aws_id], instance_id, new_resource.device, new_resource.timeout) + # always use a symbol here, it is a Hash + node.set['aws']['ebs_volume'][new_resource.name]['volume_id'] = vol[:aws_id] + node.save unless Chef::Config[:solo] + end + end +end + +action :detach do + vol = determine_volume + return if vol[:aws_instance_id] != instance_id + converge_by("detach volume with id: #{vol[:aws_id]}") do + detach_volume(vol[:aws_id], new_resource.timeout) + end +end + +action :snapshot do + vol = determine_volume + converge_by("would create a snapshot for volume: #{vol[:aws_id]}") do + snapshot = ec2.create_snapshot(vol[:aws_id],new_resource.description) + Chef::Log.info("Created snapshot of #{vol[:aws_id]} as #{snapshot[:aws_id]}") + end +end + +action :prune do + vol = determine_volume + old_snapshots = Array.new + Chef::Log.info "Checking for old snapshots" + ec2.describe_snapshots.sort { |a,b| b[:aws_started_at] <=> a[:aws_started_at] }.each do |snapshot| + if snapshot[:aws_volume_id] == vol[:aws_id] + Chef::Log.info "Found old snapshot #{snapshot[:aws_id]} (#{snapshot[:aws_volume_id]}) #{snapshot[:aws_started_at]}" + old_snapshots << snapshot + end + end + if old_snapshots.length > new_resource.snapshots_to_keep + old_snapshots[new_resource.snapshots_to_keep, old_snapshots.length].each do |die| + converge_by("delete snapshot with id: #{die[:aws_id]}") do + Chef::Log.info "Deleting old snapshot #{die[:aws_id]}" + ec2.delete_snapshot(die[:aws_id]) + end + end + end +end + +private + +def volume_id_in_node_data + begin + node['aws']['ebs_volume'][new_resource.name]['volume_id'] + rescue NoMethodError => e + nil + end +end + +# Pulls the volume id from the volume_id attribute or the node data and verifies that the volume actually exists +def determine_volume + vol = currently_attached_volume(instance_id, new_resource.device) + vol_id = new_resource.volume_id || volume_id_in_node_data || ( vol ? vol[:aws_id] : nil ) + raise "volume_id attribute not set and no volume id is set in the node data for this resource (which is populated by action :create) and no volume is attached at the device" unless vol_id + + # check that volume exists + vol = volume_by_id(vol_id) + raise "No volume with id #{vol_id} exists" unless vol + + vol +end + +# Retrieves information for a volume +def volume_by_id(volume_id) + ec2.describe_volumes.find{|v| v[:aws_id] == volume_id} +end + +# Returns the volume that's attached to the instance at the given device or nil if none matches +def currently_attached_volume(instance_id, device) + ec2.describe_volumes.find{|v| v[:aws_instance_id] == instance_id && v[:aws_device] == device} +end + +# Returns true if the given volume meets the resource's attributes +def volume_compatible_with_resource_definition?(volume) + if new_resource.snapshot_id =~ /vol/ + new_resource.snapshot_id(find_snapshot_id(new_resource.snapshot_id, new_resource.most_recent_snapshot)) + end + (new_resource.size.nil? || new_resource.size == volume[:aws_size]) && + (new_resource.availability_zone.nil? || new_resource.availability_zone == volume[:zone]) && + (new_resource.snapshot_id.nil? || new_resource.snapshot_id == volume[:snapshot_id]) +end + +# Creates a volume according to specifications and blocks until done (or times out) +def create_volume(snapshot_id, size, availability_zone, timeout, volume_type, piops) + availability_zone ||= instance_availability_zone + + # Sanity checks so we don't shoot ourselves. + raise "Invalid volume type: #{volume_type}" unless ['standard', 'io1'].include?(volume_type) + + # PIOPs requested. Must specify an iops param and probably won't be "low". + if volume_type == 'io1' + raise 'IOPS value not specified.' unless piops > 100 + end + + # Shouldn't see non-zero piops param without appropriate type. + if piops > 0 + raise 'IOPS param without piops volume type.' unless volume_type == 'io1' + end + + create_volume_opts = { :volume_type => volume_type } + # TODO: this may have to be casted to a string. rightaws vs aws doc discrepancy. + create_volume_opts[:iops] = piops if volume_type == 'io1' + + nv = ec2.create_volume(snapshot_id, size, availability_zone, create_volume_opts) + Chef::Log.debug("Created new volume #{nv[:aws_id]}#{snapshot_id ? " based on #{snapshot_id}" : ""}") + + # block until created + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(nv[:aws_id]) + if vol && vol[:aws_status] != "deleting" + if ["in-use", "available"].include?(vol[:aws_status]) + Chef::Log.info("Volume #{nv[:aws_id]} is available") + break + else + Chef::Log.debug("Volume is #{vol[:aws_status]}") + end + sleep 3 + else + raise "Volume #{nv[:aws_id]} no longer exists" + end + end + end + rescue Timeout::Error + raise "Timed out waiting for volume creation after #{timeout} seconds" + end + + nv[:aws_id] +end + +# Attaches the volume and blocks until done (or times out) +def attach_volume(volume_id, instance_id, device, timeout) + Chef::Log.debug("Attaching #{volume_id} as #{device}") + ec2.attach_volume(volume_id, instance_id, device) + + # block until attached + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(volume_id) + if vol && vol[:aws_status] != "deleting" + if vol[:aws_attachment_status] == "attached" + if vol[:aws_instance_id] == instance_id + Chef::Log.info("Volume #{volume_id} is attached to #{instance_id}") + break + else + raise "Volume is attached to instance #{vol[:aws_instance_id]} instead of #{instance_id}" + end + else + Chef::Log.debug("Volume is #{vol[:aws_status]}") + end + sleep 3 + else + raise "Volume #{volume_id} no longer exists" + end + end + end + rescue Timeout::Error + raise "Timed out waiting for volume attachment after #{timeout} seconds" + end +end + +# Detaches the volume and blocks until done (or times out) +def detach_volume(volume_id, timeout) + Chef::Log.debug("Detaching #{volume_id}") + vol = volume_by_id(volume_id) + orig_instance_id = vol[:aws_instance_id] + ec2.detach_volume(volume_id) + + # block until detached + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(volume_id) + if vol && vol[:aws_status] != "deleting" + if vol[:aws_instance_id] != orig_instance_id + Chef::Log.info("Volume detached from #{orig_instance_id}") + break + else + Chef::Log.debug("Volume: #{vol.inspect}") + end + else + Chef::Log.debug("Volume #{volume_id} no longer exists") + break + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for volume detachment after #{timeout} seconds" + end +end + + diff --git a/chef/cookbooks/aws/providers/elastic_ip.rb b/chef/cookbooks/aws/providers/elastic_ip.rb new file mode 100644 index 0000000..b538152 --- /dev/null +++ b/chef/cookbooks/aws/providers/elastic_ip.rb @@ -0,0 +1,90 @@ +include Opscode::Aws::Ec2 + +# Support whyrun +def whyrun_supported? + true +end + +action :associate do + addr = address(new_resource.ip) + + if addr.nil? + raise "Elastic IP #{new_resource.ip} does not exist" + elsif addr[:instance_id] == instance_id + Chef::Log.debug("Elastic IP #{new_resource.ip} is already attached to the instance") + else + converge_by("attach Elastic IP #{new_resource.ip} to the instance") do + Chef::Log.info("Attaching Elastic IP #{new_resource.ip} to the instance") + attach(new_resource.ip, new_resource.timeout) + end + end +end + +action :disassociate do + addr = address(new_resource.ip) + + if addr.nil? + Chef::Log.debug("Elastic IP #{new_resource.ip} does not exist, so there is nothing to detach") + elsif addr[:instance_id] != instance_id + Chef::Log.debug("Elastic IP #{new_resource.ip} is already detached from the instance") + else + converge_by("detach Elastic IP #{new_resource.ip} from the instance") do + Chef::Log.info("Detaching Elastic IP #{new_resource.ip} from the instance") + detach(new_resource.ip, new_resource.timeout) + end + end +end + +private + +def address(ip) + ec2.describe_addresses.find{|a| a[:public_ip] == ip} +end + +def attach(ip, timeout) + ec2.associate_address(instance_id, {:public_ip => ip}) + + # block until attached + begin + Timeout::timeout(timeout) do + while true + addr = address(ip) + if addr.nil? + raise "Elastic IP has been deleted while waiting for attachment" + elsif addr[:instance_id] == instance_id + Chef::Log.debug("Elastic IP is attached to this instance") + break + else + Chef::Log.debug("Elastic IP is currently attached to #{addr[:instance_id]}") + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for attachment after #{timeout} seconds" + end +end + +def detach(ip, timeout) + ec2.disassociate_address({:public_ip => ip}) + + # block until detached + begin + Timeout::timeout(timeout) do + while true + addr = address(ip) + if addr.nil? + Chef::Log.debug("Elastic IP has been deleted while waiting for detachment") + elsif addr[:instance_id] != instance_id + Chef::Log.debug("Elastic IP is detached from this instance") + break + else + Chef::Log.debug("Elastic IP is still attached") + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for detachment after #{timeout} seconds" + end +end diff --git a/chef/cookbooks/aws/providers/elastic_lb.rb b/chef/cookbooks/aws/providers/elastic_lb.rb new file mode 100644 index 0000000..8ce53ad --- /dev/null +++ b/chef/cookbooks/aws/providers/elastic_lb.rb @@ -0,0 +1,34 @@ +include Opscode::Aws::Ec2 + +action :register do + converge_by("add the node #{new_resource.name} to ELB") do + target_lb = elb.describe_load_balancers.find {|lb| lb[:load_balancer_name] == new_resource.name } + unless target_lb[:instances].include?(instance_id) + Chef::Log.info("Adding node to ELB #{new_resource.name}") + elb.register_instances_with_load_balancer(new_resource.name, instance_id) + else + Chef::Log.debug("Node #{instance_id} is already present in ELB instances, no action required.") + end + end +end + +action :deregister do + converge_by("remove the node #{new_resource.name} from ELB") do + target_lb = elb.describe_load_balancers.find {|lb| lb[:load_balancer_name] == new_resource.name } + if target_lb[:instances].include?(instance_id) + Chef::Log.info("Removing node from ELB #{new_resource.name}") + elb.deregister_instances_with_load_balancer(new_resource.name, instance_id) + else + Chef::Log.debug("Node #{instance_id} is not present in ELB instances, no action required.") + end + end +end + +private + +def elb + region = instance_availability_zone + region = region[0, region.length-1] + @@elb ||= RightAws::ElbInterface.new(new_resource.aws_access_key, new_resource.aws_secret_access_key, { :logger => Chef::Log, :region => region }) +end + diff --git a/chef/cookbooks/aws/providers/resource_tag.rb b/chef/cookbooks/aws/providers/resource_tag.rb new file mode 100644 index 0000000..5ff94be --- /dev/null +++ b/chef/cookbooks/aws/providers/resource_tag.rb @@ -0,0 +1,95 @@ +include Opscode::Aws::Ec2 + +action :add do + + unless @new_resource.resource_id + resource_id = @new_resource.name + else + resource_id = @new_resource.resource_id + end + + @new_resource.tags.each do |k,v| + unless @current_resource.tags.keys.include?(k) + converge_by("add tag '#{k}' with value '#{v}' on resource #{resource_id}") do + ec2.create_tags(resource_id, { k => v }) + Chef::Log.info("AWS: Added tag '#{k}' with value '#{v}' on resource #{resource_id}") + end + else + Chef::Log.debug("AWS: Resource #{resource_id} already has a tag with key '#{k}', will not add tag '#{k}' => '#{v}'") + end + end +end + +action :update do + unless @new_resource.resource_id + resource_id = @new_resource.name + else + resource_id = @new_resource.resource_id + end + + updated_tags = @current_resource.tags.merge(@new_resource.tags) + unless updated_tags.eql?(@current_resource.tags) + # tags that begin with "aws" are reserved + converge_by("Updating the following tags for resource #{resource_id} (skipping AWS tags): " + updated_tags.inspect) do + Chef::Log.info("AWS: Updating the following tags for resource #{resource_id} (skipping AWS tags): " + updated_tags.inspect) + updated_tags.delete_if { |key, value| key.to_s.match /^aws/ } + ec2.create_tags(resource_id, updated_tags) + end + else + Chef::Log.debug("AWS: Tags for resource #{resource_id} are unchanged") + end +end + +action :remove do + unless @new_resource.resource_id + resource_id = @new_resource.name + else + resource_id = @new_resource.resource_id + end + + tags_to_delete = @new_resource.tags.keys + + tags_to_delete.each do |key| + if @current_resource.tags.keys.include?(key) and @current_resource.tags[key] == @new_resource.tags[key] + converge_by("delete tag '#{key}' on resource #{resource_id} with value '#{@current_resource.tags[key]}'") do + ec2.delete_tags(resource_id, {key => @new_resource.tags[key]}) + Chef::Log.info("AWS: Deleted tag '#{key}' on resource #{resource_id} with value '#{@current_resource.tags[key]}'") + end + end + end +end + +action :force_remove do + unless @new_resource.resource_id + resource_id = @new_resource.name + else + resource_id = @new_resource.resource_id + end + + @new_resource.tags.keys do |key| + if @current_resource.tags.keys.include?(key) + converge_by("AWS: Deleted tag '#{key}' on resource #{resource_id} with value '#{@current_resource.tags[key]}'") do + ec2.delete_tags(resource_id, key) + Chef::Log.info("AWS: Deleted tag '#{key}' on resource #{resource_id} with value '#{@current_resource.tags[key]}'") + end + end + end +end + +def load_current_resource + @current_resource = Chef::Resource::AwsResourceTag.new(@new_resource.name) + @current_resource.name(@new_resource.name) + unless @new_resource.resource_id + @current_resource.resource_id(@new_resource.name) + else + @current_resource.resource_id(@new_resource.resource_id) + end + + @current_resource.tags(Hash.new) + + ec2.describe_tags(:filters => { 'resource-id' => @current_resource.resource_id }).map { + |tag| @current_resource.tags[tag[:key]] = tag[:value] + } + + @current_resource +end diff --git a/chef/cookbooks/aws/providers/s3_file.rb b/chef/cookbooks/aws/providers/s3_file.rb new file mode 100644 index 0000000..0aa4bea --- /dev/null +++ b/chef/cookbooks/aws/providers/s3_file.rb @@ -0,0 +1,58 @@ + +def whyrun_supported? + true +end + +action :create do + do_s3_file(:create) +end + +action :create_if_missing do + do_s3_file(:create_if_missing) +end + +action :delete do + do_s3_file(:delete) +end + +action :touch do + do_s3_file(:touch) +end + + +def do_s3_file(resource_action) + version = Chef::Version.new(Chef::VERSION[/^(\d+\.\d+\.\d+)/, 1]) + if version.major < 11 || (version.major == 11 && version.minor < 6) + Chef::Log.warn("In order to automatically use etag support to prevent re-downloading files from s3, you must upgrade to at least chef 11.6.0") + end + + remote_path = new_resource.remote_path + remote_path.sub!(/^\/*/, "") + + s3url = RightAws::S3Interface.new(new_resource.aws_access_key_id, new_resource.aws_secret_access_key).get_link(new_resource.bucket, remote_path) + + r = remote_file new_resource.name do + path new_resource.path + source s3url + owner new_resource.owner + group new_resource.group + mode new_resource.mode + checksum new_resource.checksum + backup new_resource.backup + if node['platform_family'] == "windows" + inherits new_resource.inherits + rights new_resource.rights + end + action resource_action + + if version.major > 11 || (version.major == 11 && version.minor >= 6) + headers new_resource.headers + use_etag new_resource.use_etag + use_last_modified new_resource.use_last_modified + atomic_update new_resource.atomic_update + force_unlink new_resource.force_unlink + manage_symlink_source new_resource.manage_symlink_source + end + end + new_resource.updated_by_last_action(r.updated_by_last_action?) +end diff --git a/chef/cookbooks/aws/recipes/default.rb b/chef/cookbooks/aws/recipes/default.rb new file mode 100644 index 0000000..99f1ffb --- /dev/null +++ b/chef/cookbooks/aws/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: aws +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +chef_gem "right_aws" do + version node['aws']['right_aws_version'] + action :install +end + +require 'right_aws' diff --git a/chef/cookbooks/aws/resources/ebs_raid.rb b/chef/cookbooks/aws/resources/ebs_raid.rb new file mode 100644 index 0000000..1ae486c --- /dev/null +++ b/chef/cookbooks/aws/resources/ebs_raid.rb @@ -0,0 +1,17 @@ +actions :auto_attach + +default_action :auto_attach + +attribute :mount_point, :kind_of => String +attribute :mount_point_owner, :kind_of => String, :default => 'root' +attribute :mount_point_group, :kind_of => String, :default => 'root' +attribute :mount_point_mode, :kind_of => String, :default => 00755 +attribute :disk_count, :kind_of => Integer +attribute :disk_size, :kind_of => Integer +attribute :level, :default => 10 +attribute :filesystem, :default => "ext4" +attribute :filesystem_options, :default => "rw,noatime,nobootwait" +attribute :snapshots, :default => [] +attribute :disk_type, :kind_of => String, :default => 'standard' +attribute :disk_piops, :kind_of => Integer, :default => 0 + diff --git a/chef/cookbooks/aws/resources/ebs_volume.rb b/chef/cookbooks/aws/resources/ebs_volume.rb new file mode 100644 index 0000000..0329eee --- /dev/null +++ b/chef/cookbooks/aws/resources/ebs_volume.rb @@ -0,0 +1,20 @@ +actions :create, :attach, :detach, :snapshot, :prune + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :size, :kind_of => Integer +attribute :snapshot_id, :kind_of => String +attribute :most_recent_snapshot, :kind_of => [TrueClass, FalseClass], :default => false +attribute :availability_zone, :kind_of => String +attribute :device, :kind_of => String +attribute :volume_id, :kind_of => String +attribute :description, :kind_of => String +attribute :timeout, :default => 3*60 # 3 mins, nil or 0 for no timeout +attribute :snapshots_to_keep, :default => 2 +attribute :volume_type, :kind_of => String, :default => 'standard' +attribute :piops, :kind_of => Integer, :default => 0 + +def initialize(*args) + super + @action = :create +end diff --git a/chef/cookbooks/aws/resources/elastic_ip.rb b/chef/cookbooks/aws/resources/elastic_ip.rb new file mode 100644 index 0000000..3ff8954 --- /dev/null +++ b/chef/cookbooks/aws/resources/elastic_ip.rb @@ -0,0 +1,11 @@ +actions :associate, :disassociate + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :ip, :kind_of => String +attribute :timeout, :default => 3*60 # 3 mins, nil or 0 for no timeout + +def initialize(*args) + super + @action = :associate +end diff --git a/chef/cookbooks/aws/resources/elastic_lb.rb b/chef/cookbooks/aws/resources/elastic_lb.rb new file mode 100644 index 0000000..81d95dd --- /dev/null +++ b/chef/cookbooks/aws/resources/elastic_lb.rb @@ -0,0 +1,10 @@ +actions :register, :deregister + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :name, :kind_of => String + +def initialize(*args) + super + @action = :register +end diff --git a/chef/cookbooks/aws/resources/resource_tag.rb b/chef/cookbooks/aws/resources/resource_tag.rb new file mode 100644 index 0000000..dd1375f --- /dev/null +++ b/chef/cookbooks/aws/resources/resource_tag.rb @@ -0,0 +1,11 @@ +def initialize(*args) + super + @action = :update +end + +actions :add, :update, :remove, :force_remove + +attribute :aws_access_key, :kind_of => String, :required => true +attribute :aws_secret_access_key, :kind_of => String, :required => true +attribute :resource_id, :kind_of => [ String, Array ], :regex => /(i|snap|vol)-[a-zA-Z0-9]+/ +attribute :tags, :kind_of => Hash, :required => true diff --git a/chef/cookbooks/aws/resources/s3_file.rb b/chef/cookbooks/aws/resources/s3_file.rb new file mode 100644 index 0000000..e17dbca --- /dev/null +++ b/chef/cookbooks/aws/resources/s3_file.rb @@ -0,0 +1,31 @@ +actions :create, :create_if_missing, :touch, :delete +attribute :path, :kind_of => String, :name_attribute => true +attribute :remote_path, :kind_of => String +attribute :bucket, :kind_of => String +attribute :aws_access_key_id, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :owner, :regex => Chef::Config[:user_valid_regex] +attribute :group, :regex => Chef::Config[:group_valid_regex] +attribute :mode, :kind_of => [String, NilClass], :default => nil +attribute :checksum, :kind_of => [String, NilClass], :default => nil +attribute :backup, :kind_of => [Integer, FalseClass], :default => 5 +if node['platform_family'] == "windows" + attribute :inherits, :kind_of => [TrueClass, FalseClass], :default => true + attribute :rights, :kind_of => Hash, :default => nil +end + +version = Chef::Version.new(Chef::VERSION[/^(\d+\.\d+\.\d+)/, 1]) +if version.major > 11 || (version.major == 11 && version.minor >= 6) + attribute :headers, :kind_of => Hash, :default => nil + attribute :use_etag, :kind_of => [TrueClass, FalseClass], :default => true + attribute :use_last_modified, :kind_of => [TrueClass, FalseClass], :default => true + attribute :atomic_update, :kind_of => [TrueClass, FalseClass], :default => true + attribute :force_unlink, :kind_of => [TrueClass, FalseClass], :default => false + attribute :manage_symlink_source, :kind_of => [TrueClass, FalseClass], :default => nil +end + +def initialize(*args) + super + @action = :create + @path = name +end diff --git a/chef/cookbooks/build-essential/.kitchen.yml b/chef/cookbooks/build-essential/.kitchen.yml new file mode 100644 index 0000000..9fef833 --- /dev/null +++ b/chef/cookbooks/build-essential/.kitchen.yml @@ -0,0 +1,55 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: debian-6 + driver_config: + box: opscode-debian-6 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-6.0.7_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-13.04 + driver_config: + box: opscode-ubuntu-13.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-12.10 + driver_config: + box: opscode-ubuntu-12.10 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.10_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: + - recipe[build-essential] diff --git a/chef/cookbooks/build-essential/Berksfile b/chef/cookbooks/build-essential/Berksfile new file mode 100644 index 0000000..f08b074 --- /dev/null +++ b/chef/cookbooks/build-essential/Berksfile @@ -0,0 +1,7 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" +end diff --git a/chef/cookbooks/build-essential/CHANGELOG.md b/chef/cookbooks/build-essential/CHANGELOG.md new file mode 100644 index 0000000..966a596 --- /dev/null +++ b/chef/cookbooks/build-essential/CHANGELOG.md @@ -0,0 +1,54 @@ +build-essential Cookbook CHANGELOG +================================== +This file is used to list changes made in each version of the build-essential cookbook. + +v1.4.2 +------ +### Bug +- **[COOK-3318](https://tickets.opscode.com/browse/COOK-3318)** - Use Mixlib::ShellOut instead of Chef::ShellOut + +### New Feature +- **[COOK-3093](https://tickets.opscode.com/browse/COOK-3093)** - Add OmniOS support + +### Improvement +- **[COOK-3024](https://tickets.opscode.com/browse/COOK-3024)** - Use newer package on SmartOS + +v1.4.0 +------ +This version splits up the default recipe into recipes included based on the node's platform_family. + +- [COOK-2505] - backport omnibus builder improvements + +v1.3.4 +------ +- [COOK-2272] - Complete `platform_family` conversion in build-essential + +v1.3.2 +------ +- [COOK-2069] - build-essential will install osx-gcc-installer when XCode is present + +v1.3.0 +------ +- [COOK-1895] - support smartos + +v1.2.0 +------ +- Add test-kitchen support (source repo only) +- [COOK-1677] - build-essential cookbook support for OpenSuse and SLES +- [COOK-1718] - build-essential cookbook metadata should include scientific +- [COOK-1768] - The apt-get update in build-essentials needs to be renamed + +v1.1.2 +------ +- [COOK-1620] - support OS X 10.8 + +v1.1.0 +------ +- [COOK-1098] - support amazon linux +- [COOK-1149] - support Mac OS X +- [COOK-1296] - allow for compile-time installation of packages through an attribute (see README) + +v1.0.2 +------ +- [COOK-1098] - Add Amazon Linux platform support +- [COOK-1149] - Add OS X platform support diff --git a/chef/cookbooks/build-essential/CONTRIBUTING b/chef/cookbooks/build-essential/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/build-essential/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/build-essential/LICENSE b/chef/cookbooks/build-essential/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/build-essential/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/build-essential/README.md b/chef/cookbooks/build-essential/README.md new file mode 100644 index 0000000..95a3acb --- /dev/null +++ b/chef/cookbooks/build-essential/README.md @@ -0,0 +1,147 @@ +Description +=========== + +Installs packages required for compiling C software from source. Use +this cookbook if you wish to compile C programs, or install RubyGems +with native extensions. + +Requirements +============ + +Chef version 0.10.10+ and Ohai 0.6.12+ are required. + +## Platform + +Supported platforms by platform family: + +* debian (debian, ubuntu) +* fedora +* mac_os_x (10.6+) +* rhel (centos, redhat, amazon, scientific) +* smartos +* solaris2 +* omnios + +**Note for OmniOS**: Currently, OmniOS's Ruby package is built with +GCC 4.6.3, and the path is hardcoded, as the gcc binaries are not +installed in the default $PATH. This means that in order to install +RubyGems into the "system" Ruby, one must install `developer/gcc46`. +[An issue](https://github.com/omniti-labs/omnios-build/issues/19) is +open upstream w/ OmniOS to rebuild the Ruby package with GCC 4.7.2. + +## Cookbooks + +This cookbook suggests the following external cookbooks: + +* [pkgin](http://community.opscode.com/cookbooks/pkgin) (someara) - SmartOS only +* [pkgutil](http://community.opscode.com/cookbooks/pkgutil) (marthag) - Solaris 2 only + +Attributes +========== + +* `node['build_essential']['compiletime']` - Whether the resources in +the default recipe should be configured at the "Compile" phase of the +Chef run. Defaults to false, see __Usage__ for more information. +* `node['build_essential']['osx']['gcc_installer_url']` - The URL of + the OS X GCC package installer (.pkg). +* `node['build_essential']['osx']['gcc_installer_checksum']` - The + SHA256 checksum of the OS X GCC installer. + +Recipes +======= + +The main entrypoint for this cookbook is the `default` recipe. This +recipe includes a platform specific recipe based on the node's platform +family. + +On Linux platforms (see __Platform__ above for a supported list of +families), packages required to build C source projects are installed. +This includes GCC, make, autconf and others. On Debian-family +distributions, the apt-cache may need to be updated, especially during +compile time installation. See __Usage__ for further information. + +On Mac OS X, the GCC standalone installer by Kenneth Reitz is +installed. Note that this is *not* the Xcode CLI package, as that does +not include all programs and headers required to build some common +GNU-style C projects, such as those that are available from projects +such as MacPorts or Homebrew. Changing the attributes for the GCC +installer URL and checksum to the Xcode values may work, but this is +untested. + +Usage +===== + +Simply include the `build-essential` and the required tools will be +installed to the system, and later recipes will be able to compile +software from C source code. + +For RubyGems that include native C extensions you wish to use with +Chef, you should do two things. + +0. Ensure that the C libraries, include files and other assorted "dev" +type packages are installed. You should do this in the compile phase +after the build-essential recipe. +1. Use the `chef_gem` resource in your recipes. This requires Chef version 0.10.10+. +2. Set the `compiletime` attribute in roles where such recipes are +required. This will ensure that the build tools are available to +compile the RubyGems' extensions, as `chef_gem` happens during the +compile phase, too. + +Example installation of a devel package at compile-time in a recipe: + + package "mypackage-dev" do + action :nothing + end.run_action(:install) + +Example use of `chef_gem`: + + chef_gem "mygem" + +Example role: + + name "myapp" + run_list( + "recipe[build-essential]", + "recipe[myapp]" + ) + default_attributes( + "build_essential" => { + "compiletime" => true + } + ) + +The compile time option (via the attribute) is to ensure that the +proper packages are available at the right time in the Chef run. It is +recommended that the build-essential recipe appear early in the run +list. + +The Chef wiki has documentation on +[the anatomy of a chef run](http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run). + +Limitations +=========== + +It is not in the scope of this cookbook to handle installing the +required headers for individual software projects in order to compile +them, or to compile RubyGems with native C extensions. You should +create a cookbook for handling that. + +License and Author +================== + +Author:: Joshua Timberman () +Author:: Seth Chisamore () + +Copyright 2009-2011, Opscode, Inc. () + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/build-essential/TESTING.md b/chef/cookbooks/build-essential/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/build-essential/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/build-essential/attributes/default.rb b/chef/cookbooks/build-essential/attributes/default.rb new file mode 100644 index 0000000..850f65d --- /dev/null +++ b/chef/cookbooks/build-essential/attributes/default.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: build-essential +# Attributes:: default +# +# Copyright 2008-2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['build_essential']['compiletime'] = false +#default['build_essential']['compiletime'] = true + +case node['platform_family'] +when "mac_os_x" + case + when Chef::VersionConstraint.new("~> 10.7.0").include?(node['platform_version']), + Chef::VersionConstraint.new("~> 10.8.0").include?(node['platform_version']) + default['build_essential']['osx']['gcc_installer_url'] = "https://github.com/downloads/kennethreitz/osx-gcc-installer/GCC-10.7-v2.pkg" + default['build_essential']['osx']['gcc_installer_checksum'] = "df36aa87606feb99d0db9ac9a492819e" + when Chef::VersionConstraint.new("~> 10.6.0").include?(node['platform_version']) + default['build_essential']['osx']['gcc_installer_url'] = "https://github.com/downloads/kennethreitz/osx-gcc-installer/GCC-10.6.pkg" + default['build_essential']['osx']['gcc_installer_checksum'] = "d1db5bab6a3f6b9f3b5577a130baeefa" + end +end diff --git a/chef/cookbooks/build-essential/metadata.rb b/chef/cookbooks/build-essential/metadata.rb new file mode 100644 index 0000000..ac1ac4e --- /dev/null +++ b/chef/cookbooks/build-essential/metadata.rb @@ -0,0 +1,15 @@ +name "build-essential" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs C compiler / build tools" +version "1.4.2" +recipe "build-essential", "Installs packages required for compiling C software from source." + +%w{ fedora redhat centos ubuntu debian amazon suse scientific oracle smartos}.each do |os| + supports os +end + +supports "mac_os_x", ">= 10.6.0" +supports "mac_os_x_server", ">= 10.6.0" +suggests "pkgutil" # Solaris 2 diff --git a/chef/cookbooks/build-essential/recipes/debian.rb b/chef/cookbooks/build-essential/recipes/debian.rb new file mode 100644 index 0000000..39df6c3 --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/debian.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: build-essential +# Recipe:: debian +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# on apt-based platforms when first provisioning we need to force +# apt-get update at compiletime if we are going to try to install at compiletime +execute "apt-get-update-build-essentials" do + command "apt-get update" + action :nothing + # tip: to suppress this running every time, just use the apt cookbook + not_if do + ::File.exists?('/var/lib/apt/periodic/update-success-stamp') && + ::File.mtime('/var/lib/apt/periodic/update-success-stamp') > Time.now - 86400*2 + end +end.run_action(:run) if node['build_essential']['compiletime'] + +%w{ + autoconf + binutils-doc + bison + build-essential + flex +}.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/build-essential/recipes/default.rb b/chef/cookbooks/build-essential/recipes/default.rb new file mode 100644 index 0000000..75e8f00 --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/default.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: build-essential +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +begin + include_recipe "build-essential::#{node['platform_family']}" +rescue Chef::Exceptions::RecipeNotFound + Chef::Log.warn "A build-essential recipe does not exist for the platform_family: #{node['platform_family']}" +end diff --git a/chef/cookbooks/build-essential/recipes/fedora.rb b/chef/cookbooks/build-essential/recipes/fedora.rb new file mode 100644 index 0000000..88c0d97 --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/fedora.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: build-essential +# Recipe:: fedora +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ + autoconf + bison + flex + gcc + gcc-c++ + kernel-devel + make + m4 +}.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/build-essential/recipes/mac_os_x.rb b/chef/cookbooks/build-essential/recipes/mac_os_x.rb new file mode 100644 index 0000000..14c6b1a --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/mac_os_x.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: build-essential +# Recipe:: mac_os_x +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +result = Mixlib::ShellOut.new("pkgutil --pkgs").run_command +osx_gcc_installer_installed = result.stdout.split("\n").include?("com.apple.pkg.gcc4.2Leo") +developer_tools_cli_installed = result.stdout.split("\n").include?("com.apple.pkg.DeveloperToolsCLI") +pkg_filename = ::File.basename(node['build_essential']['osx']['gcc_installer_url']) +pkg_path = "#{Chef::Config[:file_cache_path]}/#{pkg_filename}" + +r = remote_file pkg_path do + source node['build_essential']['osx']['gcc_installer_url'] + checksum node['build_essential']['osx']['gcc_installer_checksum'] + action( node['build_essential']['compiletime'] ? :nothing : :create ) + not_if { osx_gcc_installer_installed or developer_tools_cli_installed } +end +r.run_action(:create) if node['build_essential']['compiletime'] + +r = execute "sudo installer -pkg \"#{pkg_path}\" -target /" do + action( node['build_essential']['compiletime'] ? :nothing : :run ) + not_if { osx_gcc_installer_installed or developer_tools_cli_installed } +end +r.run_action(:run) if node['build_essential']['compiletime'] diff --git a/chef/cookbooks/build-essential/recipes/omnios.rb b/chef/cookbooks/build-essential/recipes/omnios.rb new file mode 100644 index 0000000..e5484a0 --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/omnios.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: build-essential +# Recipe:: omnios +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ + developer/gcc47 + developer/object-file + developer/linker + developer/library/lint + developer/build/gnu-make + system/header + system/library/math/header-math +}.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + + # Per OmniOS documentation, the gcc bin dir isn't in the default + # $PATH, so add it to the running process environment + # http://omnios.omniti.com/wiki.php/DevEnv + ENV['PATH'] = "#{ENV['PATH']}:/opt/gcc-4.7.2/bin" +end diff --git a/chef/cookbooks/build-essential/recipes/rhel.rb b/chef/cookbooks/build-essential/recipes/rhel.rb new file mode 100644 index 0000000..0d1fbac --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/rhel.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: build-essential +# Recipe:: rhel +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +pkgs = %w{ + autoconf + bison + flex + gcc + gcc-c++ + kernel-devel + make + m4 +} + +# ensure GCC 4 is available on older pre-6 EL +if node['platform_version'].to_i < 6 + pkgs.unshift %w{ gcc44 gcc44-c++ } +end + +pkgs.flatten.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/build-essential/recipes/smartos.rb b/chef/cookbooks/build-essential/recipes/smartos.rb new file mode 100644 index 0000000..a3563eb --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/smartos.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: build-essential +# Recipe:: smartos +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ + build-essential +}.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/build-essential/recipes/solaris2.rb b/chef/cookbooks/build-essential/recipes/solaris2.rb new file mode 100644 index 0000000..83e4e5e --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/solaris2.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: build-essential +# Recipe:: solaris2 +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ + autoconf + automake + bison + coreutils + flex + gcc4core + gcc4g++ + gcc4objc + gcc3core + gcc3g++ + ggrep + gmake + gtar + pkgconfig +}.each do |pkg| + + r = pkgutil_package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/build-essential/recipes/suse.rb b/chef/cookbooks/build-essential/recipes/suse.rb new file mode 100644 index 0000000..914ccd6 --- /dev/null +++ b/chef/cookbooks/build-essential/recipes/suse.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: build-essential +# Recipe:: suse +# +# Copyright 2008-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ + autoconf + bison + flex + gcc + gcc-c++ + kernel-default-devel + make + m4 +}.each do |pkg| + + r = package pkg do + action( node['build_essential']['compiletime'] ? :nothing : :install ) + end + r.run_action(:install) if node['build_essential']['compiletime'] + +end diff --git a/chef/cookbooks/chef_handler/CHANGELOG.md b/chef/cookbooks/chef_handler/CHANGELOG.md new file mode 100644 index 0000000..a249ec3 --- /dev/null +++ b/chef/cookbooks/chef_handler/CHANGELOG.md @@ -0,0 +1,28 @@ +## v1.1.4: + +* [COOK-2146] - style updates + +## v1.1.2: + +* [COOK-1989] - fix scope for handler local variable to the enable block + +## v1.1.0: + +* [COOK-1645] - properly delete old handlers +* [COOK-1322] - support platforms that use 'wheel' as root group' + +## v1.0.8: + +* [COOK-1177] - doesn't work on windows due to use of unix specific attributes +## v1.0.6: + +* [COOK-1069] - typo in chef_handler readme + +## v1.0.4: + +* [COOK-654] dont try and access a class before it has been loaded +* fix bad boolean check (if vs unless) + +## v1.0.2: + +* [COOK-620] ensure handler code is reloaded during daemonized chef runs diff --git a/chef/cookbooks/chef_handler/CONTRIBUTING b/chef/cookbooks/chef_handler/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/chef_handler/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/chef_handler/LICENSE b/chef/cookbooks/chef_handler/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/chef_handler/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/chef_handler/README.md b/chef/cookbooks/chef_handler/README.md new file mode 100644 index 0000000..ab99822 --- /dev/null +++ b/chef/cookbooks/chef_handler/README.md @@ -0,0 +1,103 @@ +Description +=========== + +Creates a configured handler path for distributing [Chef report and exception handlers](http://wiki.opscode.com/display/chef/Exception+and+Report+Handlers). Also exposes an LWRP for enabling Chef handlers from within recipe code (as opposed to hard coding in the client.rb file). This is useful for cookbook authors who may want to ship a product specific handler (see the `cloudkick` cookbook for an example) with their cookbook. + +Attributes +========== + +`node["chef_handler"]["handler_path"]` - location to drop off handlers directory, default is `/var/chef/handlers`. + +Resource/Provider +================= + +`chef_handler` +-------------- + +Requires, configures and enables handlers on the node for the current Chef run. Also has the ability to pass arguments to the handlers initializer. This allows initialization data to be pulled from a node's attribute data. + +It is best to declare `chef_handler` resources early on in the compile phase so they are available to fire for any exceptions during the Chef run. If you have a base role you would want any recipes that register Chef handlers to come first in the run_list. + +### Actions + +- :enable: Enables the Chef handler for the current Chef run on the current node +- :disable: Disables the Chef handler for the current Chef run on the current node + +### Attribute Parameters + +- class_name: name attribute. The name of the handler class (can be module name-spaced). +- source: full path to the handler file. can also be a gem path if the handler ships as part of a Ruby gem. +- arguments: an array of arguments to pass the handler's class initializer +- supports: type of Chef Handler to register as, ie :report, :exception or both. default is `:report => true, :exception => true` + +### Example + + # register the Chef::Handler::JsonFile handler + # that ships with the Chef gem + chef_handler "Chef::Handler::JsonFile" do + source "chef/handler/json_file" + arguments :path => '/var/chef/reports' + action :enable + end + + # do the same but during the compile phase + chef_handler "Chef::Handler::JsonFile" do + source "chef/handler/json_file" + arguments :path => '/var/chef/reports' + action :nothing + end.run_action(:enable) + + # handle exceptions only + chef_handler "Chef::Handler::JsonFile" do + source "chef/handler/json_file" + arguments :path => '/var/chef/reports' + supports :exception => true + action :enable + end + + + # enable the CloudkickHandler which was + # dropped off in the default handler path. + # passes the oauth key/secret to the handler's + # intializer. + chef_handler "CloudkickHandler" do + source "#{node['chef_handler']['handler_path']}/cloudkick_handler.rb" + arguments [node['cloudkick']['oauth_key'], node['cloudkick']['oauth_secret']] + action :enable + end + + +Usage +===== + +default +------- + +Put the recipe `chef_handler` at the start of the node's run list to make sure that custom handlers are dropped off early on in the Chef run and available for later recipes. + +For information on how to write report and exception handlers for Chef, please see the Chef wiki pages: +http://wiki.opscode.com/display/chef/Exception+and+Report+Handlers + +json_file +--------- + +Leverages the `chef_handler` LWRP to automatically register the `Chef::Handler::JsonFile` handler that ships as part of Chef. This handler serializes the run status data to a JSON file located at `/var/chef/reports`. + +License and Author +================== + +Author:: Seth Chisamore () + +Copyright:: 2011, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/chef_handler/attributes/default.rb b/chef/cookbooks/chef_handler/attributes/default.rb new file mode 100644 index 0000000..19d2fec --- /dev/null +++ b/chef/cookbooks/chef_handler/attributes/default.rb @@ -0,0 +1,30 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: chef_handlers +# Attribute:: default +# +# Copyright 2011-2013, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default["chef_handler"]["root_user"] = "root" + +case platform +when "openbsd", "freebsd", "mac_os_x", "mac_os_x_server" + default["chef_handler"]["root_group"] = "wheel" +else + default["chef_handler"]["root_group"] = "root" +end + +default["chef_handler"]["handler_path"] = "#{File.expand_path(File.join(Chef::Config[:file_cache_path], '..'))}/handlers" diff --git a/chef/cookbooks/chef_handler/files/default/handlers/README b/chef/cookbooks/chef_handler/files/default/handlers/README new file mode 100644 index 0000000..b575066 --- /dev/null +++ b/chef/cookbooks/chef_handler/files/default/handlers/README @@ -0,0 +1 @@ +This directory contains Chef handlers to distribute to your nodes. diff --git a/chef/cookbooks/chef_handler/metadata.rb b/chef/cookbooks/chef_handler/metadata.rb new file mode 100644 index 0000000..703ae11 --- /dev/null +++ b/chef/cookbooks/chef_handler/metadata.rb @@ -0,0 +1,7 @@ +name "chef_handler" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Distribute and enable Chef Exception and Report handlers" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.4" diff --git a/chef/cookbooks/chef_handler/providers/default.rb b/chef/cookbooks/chef_handler/providers/default.rb new file mode 100644 index 0000000..2eb2be7 --- /dev/null +++ b/chef/cookbooks/chef_handler/providers/default.rb @@ -0,0 +1,93 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: chef_handler +# Provider:: default +# +# Copyright:: 2011-2013, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def whyrun_supported? + true +end + +action :enable do + # use load instead of require to ensure the handler file + # is reloaded into memory each chef run. fixes COOK-620 + handler = nil + converge_by("load #{@new_resource.source}") do + begin + Object.send(:remove_const, klass) + GC.start + rescue + Chef::Log.debug("#{@new_resource.class_name} has not been loaded.") + end + file_name = @new_resource.source + file_name << ".rb" unless file_name =~ /.*\.rb$/ + load file_name + handler = klass.send(:new, *collect_args(@new_resource.arguments)) + end + @new_resource.supports.each do |type, enable| + if enable + # we have to re-enable the handler every chef run + # to ensure daemonized Chef always has the latest + # handler code. TODO: add a :reload action + converge_by("enable #{@new_resource} as a #{type} handler") do + Chef::Log.info("Enabling #{@new_resource} as a #{type} handler") + Chef::Config.send("#{type.to_s}_handlers").delete_if { |v| v.class.to_s.include? @new_resource.class_name.split('::', 3).last } + Chef::Config.send("#{type.to_s}_handlers") << handler + end + end + end +end + +action :disable do + @new_resource.supports.each_key do |type| + if enabled?(type) + converge_by("disable #{@new_resource} as a #{type} handler") do + Chef::Log.info("Disabling #{@new_resource} as a #{type} handler") + Chef::Config.send("#{type.to_s}_handlers").delete_if { |v| v.class.to_s.include? @new_resource.class_name.split('::', 3).last } + end + end + end +end + +def load_current_resource + @current_resource = Chef::Resource::ChefHandler.new(@new_resource.name) + @current_resource.class_name(@new_resource.class_name) + @current_resource.source(@new_resource.source) + @current_resource +end + +private + +def enabled?(type) + Chef::Config.send("#{type.to_s}_handlers").select do |handler| + handler.class.to_s.include? @new_resource.class_name + end.size >= 1 +end + +def collect_args(resource_args = []) + if resource_args.is_a? Array + resource_args + else + [resource_args] + end +end + +def klass + @klass ||= begin + @new_resource.class_name.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name) } + end +end diff --git a/chef/cookbooks/chef_handler/recipes/default.rb b/chef/cookbooks/chef_handler/recipes/default.rb new file mode 100644 index 0000000..540a5ff --- /dev/null +++ b/chef/cookbooks/chef_handler/recipes/default.rb @@ -0,0 +1,33 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: chef_handlers +# Recipe:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +Chef::Log.info("Chef Handlers will be at: #{node['chef_handler']['handler_path']}") + +remote_directory node['chef_handler']['handler_path'] do + source 'handlers' + # Just inherit permissions on Windows, don't try to set POSIX perms + if node["platform"] != "windows" + owner node['chef_handler']['root_user'] + group node['chef_handler']['root_group'] + mode "0755" + recursive true + end + action :nothing +end.run_action(:create) diff --git a/chef/cookbooks/chef_handler/recipes/json_file.rb b/chef/cookbooks/chef_handler/recipes/json_file.rb new file mode 100644 index 0000000..d2fab10 --- /dev/null +++ b/chef/cookbooks/chef_handler/recipes/json_file.rb @@ -0,0 +1,28 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: chef_handlers +# Recipe:: json_file +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# force resource actions in compile phase so exception handler +# fires for compile phase exceptions + +chef_handler "Chef::Handler::JsonFile" do + source "chef/handler/json_file" + arguments :path => '/var/chef/reports' + action :nothing +end.run_action(:enable) diff --git a/chef/cookbooks/chef_handler/resources/default.rb b/chef/cookbooks/chef_handler/resources/default.rb new file mode 100644 index 0000000..f74aafa --- /dev/null +++ b/chef/cookbooks/chef_handler/resources/default.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: chef_handler +# Resource:: default +# +# Copyright:: 2011-2013, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :enable, :disable + +attribute :class_name, :kind_of => String, :name_attribute => true +attribute :source, :default => nil, :kind_of => String +attribute :arguments, :default => [] +attribute :supports, :kind_of => Hash, :default => { :report => true, :exception => true } + +# we have to set default for the supports attribute +# in initializer since it is a 'reserved' attribute name +def initialize(*args) + super + @action = :enable + @supports = { :report => true, :exception => true } +end diff --git a/chef/cookbooks/database/CHANGELOG.md b/chef/cookbooks/database/CHANGELOG.md new file mode 100644 index 0000000..d76e795 --- /dev/null +++ b/chef/cookbooks/database/CHANGELOG.md @@ -0,0 +1,82 @@ +## v1.4.0: + +### Bug + +- [COOK-2074]: Regex in exists? check in `sql_server_database` resource + should match for start and end of line +- [COOK-2561]: `mysql_database_user` can't set global grants + +### Improvement + +- [COOK-2075]: Support the collation attribute in the + `database_sql_server` provider + +## v1.3.12: + +* [COOK-850] - `postgresql_database_user` doesn't have example + +## v1.3.10: + +* [COOK-2117] - undefined variable `grant_statement` in mysql user + provider + +## v1.3.8: + +* [COOK-1896] - Escape command +* [COOK-2047] - Chef::Provider::Database::MysqlUser action :grant + improperly quotes `username`@`host` string +* [COOK-2060] - Mysql::Error: Table '*.*' doesn't exist when privileges + include SELECT and database/table attributes are nil +* [COOK-2062] - Remove backticks from database name when using wildcard + +## v1.3.6: + +* [COOK-1688] - fix typo in readme and add amazon linux to supported + platforms + +## v1.3.4: + +* [COOK-1561] - depend on mysql 1.3.0+ explicitly +* depend on postgresql 1.0.0 explicitly + +## v1.3.2: + +* Update the version for release (oops) + +## v1.3.0: + +* [COOK-932] - Add mysql recipe to conveniently include mysql::ruby +* [COOK-1228] - database resource should be able to execute scripts on disk +* [COOK-1291] - make the snapshot retention policy less confusing +* [COOK-1401] - Allow to specify the collation of new databases +* [COOK-1534] - Add postgresql recipe to conveniently include postgresql::ruby + +## v1.2.0: + +* [COOK-970] - workaround for disk [re]naming on ubuntu 11.04+ +* [COOK-1085] - check RUBY_VERSION and act accordingly for role +* [COOK-749] - localhost should be a string in snapshot recipe + +## v1.1.4: + +* [COOK-1062] - Databases: Postgres exists should close connection + +## v1.1.2: + +* [COOK-975] - Change arg='DEFAULT' to arg=nil, :default => 'DEFAULT' +* [COOK-964] - Add parentheses around connection hash in example + +## v1.1.0 + +* [COOK-716] - providers for PostgreSQL + +## v1.0.0 + +* [COOK-683] - added `database` and `database_user` resources +* [COOK-684] - MySQL providers +* [COOK-685] - SQL Server providers +* refactored - `database::master` and `database::snapshot` recipes to leverage new resources + +## v0.99.1 + +* Use Chef 0.10's `node.chef_environment` instead of `node['app_environment']`. diff --git a/chef/cookbooks/database/CONTRIBUTING b/chef/cookbooks/database/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/database/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/database/LICENSE b/chef/cookbooks/database/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/database/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/database/README.md b/chef/cookbooks/database/README.md new file mode 100644 index 0000000..a7d209e --- /dev/null +++ b/chef/cookbooks/database/README.md @@ -0,0 +1,510 @@ +Database Cookbook +================= + +The main highlight of this cookbook is the `database` and +`database_user` resources for managing databases and database users in +a RDBMS. Providers for MySQL, PostgreSQL and SQL Server are also +provided, see usage documentation below. + +This cookbook also contains recipes to configure mysql database +masters and slaves and uses EBS for storage, integrating together with +the application cookbook utilizing data bags for application related +information. These recipes are written primarily to use MySQL and the +Opscode mysql cookbook. Other RDBMS may be supported at a later date. +This cookbook does not automatically restore database dumps, but does +install tools to help with that. + +Requirements +============ + +Chef version 0.10.10+. + +Platform +-------- + +* Debian, Ubuntu +* Red Hat, CentOS, Scientific, Fedora, Amazon + +Cookbooks +--------- + +The following Opscode cookbooks are dependencies: + +* mysql +* postgresql +* xfs +* aws + +Resources/Providers +=================== + +These resources aim to expose an abstraction layer for interacting +with different RDBMS in a general way. Currently the cookbook ships +with providers for MySQL, PostgreSQL and SQL Server. Please see +specific usage in the __Example__ sections below. The providers use +specific Ruby gems installed under Chef's Ruby environment to execute +commands and carry out actions. These gems will need to be installed +before the providers can operate correctly. Specific notes for each +RDBS flavor: + +- MySQL: leverages the `mysql` gem which is installed as part of the + `mysql::ruby` recipe. You can use `database::mysql` to include this, + too. +- PostgreSQL: leverages the `pg` gem which is installed as part of the + `postgresql::ruby` recipe. You can use `database::postgresql` to + include this, too. + Currently does not work in Chef "omnibus" full stack installs, see COOK-1406. +- SQL Server: leverages the `tiny_tds` gem which is installed as part + of the `sql_server::client` recipe. + +`database` +---------- + +Manage databases in a RDBMS. Use the proper shortcut resource +depending on your RDBMS: `mysql_database`, `postgresql_database` or +`sql_server_database`. + +### Actions + +- :create: create a named database +- :drop: drop a named database +- :query: execute an arbitrary query against a named database + +### Attribute Parameters + +- database_name: name attribute. Name of the database to interact with +- connection: hash of connection info. valid keys include :host, + :port, :username, :password +- sql: string of sql or a block that executes to a string of sql, + which will be executed against the database. used by :query action + only + +### Providers + +- **Chef::Provider::Database::Mysql**: shortcut resource `mysql_database` +- **Chef::Provider::Database::Postgresql**: shortcut resource `postgresql_database` +- **Chef::Provider::Database::SqlServer**: shortcut resource `sql_server_database` + +### Examples + + # create a mysql database + mysql_database 'oracle_rules' do + connection ({:host => "localhost", :username => 'root', :password => node['mysql']['server_root_password']}) + action :create + end + + # create a sql server database + sql_server_database 'mr_softie' do + connection ({:host => "127.0.0.1", :port => node['sql_server']['port'], :username => 'sa', :password => node['sql_server']['server_sa_password']}) + action :create + end + + # create a postgresql database + postgresql_database 'mr_softie' do + connection ({:host => "127.0.0.1", :port => 5432, :username => 'postgres', :password => node['postgresql']['password']['postgres']}) + action :create + end + + # create a postgresql database with additional parameters + postgresql_database 'mr_softie' do + connection ({:host => "127.0.0.1", :port => 5432, :username => 'postgres', :password => node['postgresql']['password']['postgres']}) + template 'DEFAULT' + encoding 'DEFAULT' + tablespace 'DEFAULT' + connection_limit '-1' + owner 'postgres' + action :create + end + + # externalize conection info in a ruby hash + mysql_connection_info = {:host => "localhost", + :username => 'root', + :password => node['mysql']['server_root_password']} + sql_server_connection_info = {:host => "localhost", + :port => node['sql_server']['port'], + :username => 'sa', + :password => node['sql_server']['server_sa_password']} + postgresql_connection_info = {:host => "127.0.0.1", + :port => node['postgresql']['config']['port'], + :username => 'postgres', + :password => node['postgresql']['password']['postgres']} + + # same create commands, connection info as an external hash + mysql_database 'foo' do + connection mysql_connection_info + action :create + end + sql_server_database 'foo' do + connection sql_server_connection_info + action :create + end + postgresql_database 'foo' do + connection postgresql_connection_info + action :create + end + + # create database, set provider in resource parameter + database 'bar' do + connection mysql_connection_info + provider Chef::Provider::Database::Mysql + action :create + end + database 'bar' do + connection sql_server_connection_info + provider Chef::Provider::Database::SqlServer + action :create + end + database 'bar' do + connection postgresql_connection_info + provider Chef::Provider::Database::Postgresql + action :create + end + + # drop a database + mysql_database "baz" do + connection mysql_connection_info + action :drop + end + + # query a database + mysql_database "flush the privileges" do + connection mysql_connection_info + sql "flush privileges" + action :query + end + + # query a database from a sql script on disk + mysql_database "run script" do + connection mysql_connection_info + sql { ::File.open("/path/to/sql_script.sql").read } + action :query + end + + # vacuum a postgres database + postgres_database "vacuum databases" do + connection postgresql_connection_info + database_table "template1" + sql "VACUUM FULL VERBOSE ANALYZE" + action :query + end + +`database_user` +--------------- + +Manage users and user privileges in a RDBMS. Use the proper shortcut +resource depending on your RDBMS: `mysql_database_user`, +`postgresql_database_user`, or `sql_server_database_user`. + +### Actions + +- :create: create a user +- :drop: drop a user +- :grant: manipulate user privileges on database objects + +### Attribute Parameters + +- username: name attribute. Name of the database user +- password: password for the user account +- database_name: Name of the database to interact with +- connection: hash of connection info. valid keys include :host, + :port, :username, :password +- privileges: array of database privileges to grant user. used by the + :grant action. default is :all +- host: host where user connections are allowed from. used by MySQL + provider only. default is 'localhost' +- table: table to grant privileges on. used by :grant action and MySQL + provider only. default is '*' (all tables) + +### Providers + +- **Chef::Provider::Database::MysqlUser**: shortcut resource + `mysql_database_user` +- **Chef::Provider::Database::PostgresqlUser**: shortcut + resource `postgresql_database_user` +- **Chef::Provider::Database::SqlServerUser**: shortcut resource + `sql_server_database_user` + +### Examples + + # create connection info as an external ruby hash + mysql_connection_info = {:host => "localhost", + :username => 'root', + :password => node['mysql']['server_root_password']} + postgresql_connection_info = {:host => "localhost", + :port => node['postgresql']['config']['port'], + :username => 'postgres', + :password => node['postgresql']['password']['postgres']} + sql_server_connection_info = {:host => "localhost", + :port => node['sql_server']['port'], + :username => 'sa', + :password => node['sql_server']['server_sa_password']} + + # create a mysql user but grant no privileges + mysql_database_user 'disenfranchised' do + connection mysql_connection_info + password 'super_secret' + action :create + end + + # do the same but pass the provider to the database resource + database_user 'disenfranchised' do + connection mysql_connection_info + password 'super_secret' + provider Chef::Provider::Database::MysqlUser + action :create + end + + # create a postgresql user but grant no privileges + postgresql_database_user 'disenfranchised' do + connection postgresql_connection_info + password 'super_secret' + action :create + end + + # do the same but pass the provider to the database resource + database_user 'disenfranchised' do + connection postgresql_connection_info + password 'super_secret' + provider Chef::Provider::Database::PostgresqlUser + action :create + end + + # create a sql server user but grant no privileges + sql_server_database_user 'disenfranchised' do + connection sql_server_connection_info + password 'super_secret' + action :create + end + + # drop a mysql user + mysql_database_user "foo_user" do + connection mysql_connection_info + action :drop + end + + # bulk drop sql server users + %w{ disenfranchised foo_user }.each do |user| + sql_server_database_user user do + connection sql_server_connection_info + action :drop + end + end + + # grant select,update,insert privileges to all tables in foo db from all hosts + mysql_database_user 'foo_user' do + connection mysql_connection_info + password 'super_secret' + database_name 'foo' + host '%' + privileges [:select,:update,:insert] + action :grant + end + + # grant all privileges on all databases/tables from localhost + mysql_database_user 'super_user' do + connection mysql_connection_info + password 'super_secret' + action :grant + end + + # grant all privileges on all tables in foo db + postgresql_database_user 'foo_user' do + connection postgresql_connection_info + database_name 'foo' + privileges [:all] + action :grant + end + + # grant select,update,insert privileges to all tables in foo db + sql_server_database_user 'foo_user' do + connection sql_server_connection_info + password 'super_secret' + database_name 'foo' + privileges [:select,:update,:insert] + action :grant + end + +Recipes +======= + +ebs\_volume +----------- + +*Note*: This recipe does not currently work on RHEL platforms due to + the xfs cookbook not supporting RHEL yet. + +Loads the aws information from the data bag. Searches the applications +data bag for the database master or slave role and checks that role is +applied to the node. Loads the EBS information and the master +information from data bags. Uses the aws cookbook LWRP, +`aws_ebs_volume` to manage the volume. + +On a master node: +* if we have an ebs volume already as stored in a data bag, attach it. +* if we don't have the ebs information then create a new one and + attach it. +* store the volume information in a data bag via a ruby block. + +On a slave node: +* use the master volume information to generate a snapshot. +* create the new volume from the snapshot and attach it. + +Also on a master node, generate some configuration for running a +snapshot via `chef-solo` from cron. + +On a new filesystem volume, create as XFS, then mount it in /mnt, and +also bind-mount it to the mysql data directory (default +/var/lib/mysql). + +master +------ + +This recipe no longer loads AWS specific information, and the database +position for replication is no longer stored in a databag because the +client might not have permission to write to the databag item. This +may be handled in a different way at a future date. + +Searches the apps databag for applications, and for each one it will +check that the specified database master role is set in both the +databag and applied to the node's run list. Then, retrieves the +passwords for `root`, `repl` and `debian` users and saves them to the +node attributes. If the passwords are not found in the databag, it +prints a message that they'll be generated by the mysql cookbook. + +Then it adds the application databag database settings to a hash, to +use later. + +Then it will iterate over the databases and create them with the +`mysql_database` resource while adding privileges for application +specific database users using the `mysql_database_user` resource. + +slave +----- + +_TODO_: Retrieve the master status from a data bag, then start +replication using a ruby block. The replication status needs to be +handled in some other way for now since the master recipe above +doesn't actually set it in the databag anymore. + +snapshot +-------- + +Run via Chef Solo. Retrieves the db snapshot configuration from the +specified JSON file. Uses the `mysql_database` resource to lock and +unlock tables, and does a filesystem freeze and EBS snapshot. + +Deprecated Recipes +================== + +The following recipe is considered deprecated. It is kept for +reference purposes. + +ebs\_backup +----------- + +Older style of doing mysql snapshot and replication using Adam Jacob's +[ec2_mysql](http://github.com/adamhjk/ec2_mysql) script and library. + +Data Bags +========= + +This cookbook uses the apps data bag item for the specified +application; see the `application` cookbook's README.md. It also +creates data bag items in a bag named 'aws' for storing volume +information. In order to interact with EC2, it expects aws to have a +main item: + + { + "id": "main", + "ec2_private_key": "private key as a string", + "ec2_cert": "certificate as a string", + "aws_account_id": "", + "aws_secret_access_key": "", + "aws_access_key_id": "" + } + +Note: with the Open Source Chef Server, the server using the database +recipes must be an admin client or it will not be able to create data +bag items. You can modify whether the client is admin by editing it +with knife. + + knife client edit + { + ... + "admin": true + ... + } + +This is not required if the Chef Server is Opscode Hosted Chef, +instead use the ACL feature to modify access for the node to be able +to update the data bag. + +Usage +===== + +Aside from the application data bag (see the README in the application +cookbook), create a role for the database master. Use a role.rb in +your chef-repo, or create the role directly with knife. + + % knife role show my_app_database_master -Fj + { + "name": "my_app_database_master", + "chef_type": "role", + "json_class": "Chef::Role", + "default_attributes": { + }, + "description": "", + "run_list": [ + "recipe[mysql::server]", + "recipe[database::master]" + ], + "override_attributes": { + } + } + +Create a `production` environment. This is also used in the +`application` cookbook. + + % knife environment show production -Fj + { + "name": "production", + "description": "", + "cookbook_versions": { + }, + "json_class": "Chef::Environment", + "chef_type": "environment", + "default_attributes": { + }, + "override_attributes": { + } + } + + +The cookbook `my_app_database` is recommended to set up any +application specific database resources such as configuration +templates, trending monitors, etc. It is not required, but you would +need to create it separately in `site-cookbooks`. Add it to the +`my_app_database_master` role. + +License and Author +================== + +- Author:: Adam Jacob () +- Author:: Joshua Timberman () +- Author:: AJ Christensen () +- Author:: Seth Chisamore () +- Author:: Lamont Granquist () + +Copyright 2009-2012, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/database/libraries/provider_database_mysql.rb b/chef/cookbooks/database/libraries/provider_database_mysql.rb new file mode 100644 index 0000000..b8954a9 --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_mysql.rb @@ -0,0 +1,103 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' + +class Chef + class Provider + class Database + class Mysql < Chef::Provider + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'mysql' + @current_resource = Chef::Resource::Database.new(@new_resource.name) + @current_resource.database_name(@new_resource.database_name) + @current_resource + end + + def action_create + unless exists? + begin + Chef::Log.debug("#{@new_resource}: Creating database `#{new_resource.database_name}`") + create_sql = "CREATE DATABASE `#{new_resource.database_name}`" + create_sql += " CHARACTER SET = #{new_resource.encoding}" if new_resource.encoding + create_sql += " COLLATE = #{new_resource.collation}" if new_resource.collation + Chef::Log.debug("#{@new_resource}: Performing query [#{create_sql}]") + db.query(create_sql) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + Chef::Log.debug("#{@new_resource}: Dropping database #{new_resource.database_name}") + db.query("DROP DATABASE `#{new_resource.database_name}`") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_query + if exists? + begin + db.select_db(@new_resource.database_name) if @new_resource.database_name + Chef::Log.debug("#{@new_resource}: Performing query [#{new_resource.sql_query}]") + db.query(@new_resource.sql_query) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + private + def exists? + db.list_dbs.include?(@new_resource.database_name) + end + + def db + @db ||= begin + connection = ::Mysql.new( + @new_resource.connection[:host], + @new_resource.connection[:username], + @new_resource.connection[:password], + nil, + @new_resource.connection[:port] || 3306 + ) + connection.set_server_option ::Mysql::OPTION_MULTI_STATEMENTS_ON + connection + end + end + + def close + @db.close rescue nil + @db = nil + end + + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_mysql_user.rb b/chef/cookbooks/database/libraries/provider_database_mysql_user.rb new file mode 100644 index 0000000..56c47c4 --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_mysql_user.rb @@ -0,0 +1,86 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'provider_database_mysql') + +class Chef + class Provider + class Database + class MysqlUser < Chef::Provider::Database::Mysql + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'mysql' + @current_resource = Chef::Resource::DatabaseUser.new(@new_resource.name) + @current_resource.username(@new_resource.name) + @current_resource + end + + def action_create + unless exists? + begin + db.query("CREATE USER `#{@new_resource.username}`@`#{@new_resource.host}` IDENTIFIED BY '#{@new_resource.password}'") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + db.query("DROP USER `#{@new_resource.username}`@`#{@new_resource.host}`") + @new_resource.updated_by_last_action(true) + rescue + Chef::Log.warn("The action_drop drop user failed: drop user '#{@new_resource.username}'@'#{@new_resource.host}'") + ensure + close + end + end + end + + def action_grant + begin + # does password look like MySQL hex digest? + # (begins with *, followed by 40 hexadecimal characters) + if (/(\A\*[0-9A-F]{40}\z)/i).match(@new_resource.password) then + password = filtered = "PASSWORD '#{$1}'" + else + password = "'#{@new_resource.password}'" + filtered = '[FILTERED]' + end + grant_statement = "GRANT #{@new_resource.privileges.join(', ')} ON #{@new_resource.database_name && @new_resource.database_name != '*' ? "`#{@new_resource.database_name}`" : '*'}.#{@new_resource.table && @new_resource.table != '*' ? "`#{@new_resource.table}`" : '*'} TO `#{@new_resource.username}`@`#{@new_resource.host}` IDENTIFIED BY " + Chef::Log.info("#{@new_resource}: granting access with statement [#{grant_statement}#{filtered}]") + db.query(grant_statement + password) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + + private + def exists? + db.query("SELECT User,host from mysql.user WHERE User = '#{@new_resource.username}' AND host = '#{@new_resource.host}'").num_rows != 0 + end + + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_postgresql.rb b/chef/cookbooks/database/libraries/provider_database_postgresql.rb new file mode 100644 index 0000000..c2b5bcf --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_postgresql.rb @@ -0,0 +1,131 @@ +# +# Author:: Seth Chisamore () +# Author:: Lamont Granquist () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' + +class Chef + class Provider + class Database + class Postgresql < Chef::Provider + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'pg' + @current_resource = Chef::Resource::Database.new(@new_resource.name) + @current_resource.database_name(@new_resource.database_name) + @current_resource + end + + def action_create + unless exists? + begin + encoding = @new_resource.encoding + if encoding != "DEFAULT" + encoding = "'#{@new_resource.encoding}'" + end + Chef::Log.debug("#{@new_resource}: Creating database #{new_resource.database_name}") + create_sql = "CREATE DATABASE \"#{new_resource.database_name}\"" + create_sql += " TEMPLATE = #{new_resource.template}" if new_resource.template + create_sql += " ENCODING = #{encoding}" if new_resource.encoding + create_sql += " TABLESPACE = #{new_resource.tablespace}" if new_resource.tablespace + create_sql += " LC_CTYPE = '#{new_resource.collation}' LC_COLLATE = '#{new_resource.collation}'" if new_resource.collation + create_sql += " CONNECTION LIMIT = #{new_resource.connection_limit}" if new_resource.connection_limit + create_sql += " OWNER = \"#{new_resource.owner}\"" if new_resource.owner + Chef::Log.debug("#{@new_resource}: Performing query [#{create_sql}]") + db("template1").query(create_sql) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + Chef::Log.debug("#{@new_resource}: Dropping database #{new_resource.database_name}") + db("template1").query("DROP DATABASE \"#{new_resource.database_name}\"") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_query + if exists? + begin + Chef::Log.debug("#{@new_resource}: Performing query [#{new_resource.sql_query}]") + db(@new_resource.database_name).query(@new_resource.sql_query) + Chef::Log.debug("#{@new_resource}: query [#{new_resource.sql_query}] succeeded") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + private + + def exists? + begin + Chef::Log.debug("#{@new_resource}: checking if database #{@new_resource.database_name} exists") + ret = db("template1").query("SELECT * FROM pg_database where datname = '#{@new_resource.database_name}'").num_tuples != 0 + ret ? Chef::Log.debug("#{@new_resource}: database #{@new_resource.database_name} exists") : + Chef::Log.debug("#{@new_resource}: database #{@new_resource.database_name} does not exist") + ensure + close + end + ret + end + + # + # Specifying the database in the connection parameter for the postgres resource is not recommended. + # + # - action_create/drop/exists will use the "template1" database to do work by default. + # - action_query will use the resource database_name. + # - specifying a database in the connection will override this behavior + # + def db(dbname = nil) + close if @db + dbname = @new_resource.connection[:database] if @new_resource.connection[:database] + host = @new_resource.connection[:host] + port = @new_resource.connection[:port] || 5432 + user = @new_resource.connection[:username] || "postgres" + Chef::Log.debug("#{@new_resource}: connecting to database #{dbname} on #{host}:#{port} as #{user}") + password = @new_resource.connection[:password] || node[:postgresql][:password][:postgres] + @db = ::PGconn.new( + :host => host, + :port => port, + :dbname => dbname, + :user => user, + :password => password + ) + end + + def close + @db.close rescue nil + @db = nil + end + + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb b/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb new file mode 100644 index 0000000..5959086 --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_postgresql_user.rb @@ -0,0 +1,83 @@ +# +# Author:: Seth Chisamore () +# Author:: Lamont Granquist () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'provider_database_postgresql') + +class Chef + class Provider + class Database + class PostgresqlUser < Chef::Provider::Database::Postgresql + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'pg' + @current_resource = Chef::Resource::DatabaseUser.new(@new_resource.name) + @current_resource.username(@new_resource.name) + @current_resource + end + + def action_create + unless exists? + begin + db("template1").query("CREATE USER \"#{@new_resource.username}\" WITH PASSWORD '#{@new_resource.password}'") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + db("template1").query("DROP USER \"#{@new_resource.username}\"") + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_grant + begin + # FIXME: grants on individual tables + grant_statement = "GRANT #{@new_resource.privileges.join(', ')} ON DATABASE \"#{@new_resource.database_name}\" TO \"#{@new_resource.username}\"" + Chef::Log.info("#{@new_resource}: granting access with statement [#{grant_statement}]") + db(@new_resource.database_name).query(grant_statement) + @new_resource.updated_by_last_action(true) + ensure + close + end + end + + private + def exists? + begin + exists = db("template1").query("SELECT * FROM pg_user WHERE usename='#{@new_resource.username}'").num_tuples != 0 + ensure + close + end + exists + end + + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_sql_server.rb b/chef/cookbooks/database/libraries/provider_database_sql_server.rb new file mode 100644 index 0000000..a422ecb --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_sql_server.rb @@ -0,0 +1,111 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' + +class Chef + class Provider + class Database + class SqlServer < Chef::Provider + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'tiny_tds' + @current_resource = Chef::Resource::Database.new(@new_resource.name) + @current_resource.database_name(@new_resource.database_name) + @current_resource + end + + def action_create + unless exists? + begin + Chef::Log.debug("#{@new_resource}: Creating database #{new_resource.database_name}") + create_sql = "CREATE DATABASE [#{new_resource.database_name}]" + create_sql += " COLLATE #{new_resource.collation}" if new_resource.collation + db.execute(create_sql).do + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_drop + if exists? + begin + Chef::Log.debug("#{@new_resource}: Dropping database #{new_resource.database_name}") + db.execute("DROP DATABASE [#{new_resource.database_name}]").do + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + def action_query + if exists? + begin + #db.select_db(@new_resource.database_name) if @new_resource.database_name + Chef::Log.debug("#{@new_resource}: Performing query [#{new_resource.sql_query}]") + db.execute(@new_resource.sql_query).do + @new_resource.updated_by_last_action(true) + ensure + close + end + end + end + + private + def exists? + exists = false + begin + result = db.execute("SELECT name FROM sys.databases") + result.each do |row| + if row['name'] == @new_resource.database_name + exists = true + break + end + end + result.cancel + ensure + close + end + exists + end + + def db + @db ||= begin + ::TinyTds::Client.new( + :host => @new_resource.connection[:host], + :username => @new_resource.connection[:username], + :password => @new_resource.connection[:password], + :port => @new_resource.connection[:port] || 1433 + ) + end + end + + def close + @db.close rescue nil + @db = nil + end + + end + end + end +end diff --git a/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb b/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb new file mode 100644 index 0000000..596892e --- /dev/null +++ b/chef/cookbooks/database/libraries/provider_database_sql_server_user.rb @@ -0,0 +1,106 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'provider_database_sql_server') + +class Chef + class Provider + class Database + class SqlServerUser < Chef::Provider::Database::SqlServer + include Chef::Mixin::ShellOut + + def load_current_resource + Gem.clear_paths + require 'tiny_tds' + @current_resource = Chef::Resource::DatabaseUser.new(@new_resource.name) + @current_resource.username(@new_resource.name) + @current_resource + end + + def action_create + begin + unless exists?(:logins) + db.execute("CREATE LOGIN [#{@new_resource.username}] WITH PASSWORD = '#{@new_resource.password}', CHECK_POLICY = OFF").do + @new_resource.updated_by_last_action(true) + end + unless exists?(:users) + if @new_resource.database_name + Chef::Log.info("#{@new_resource} creating user in '#{@new_resource.database_name}' database context.") + db.execute("USE [#{@new_resource.database_name}]").do + else + Chef::Log.info("#{@new_resource} database_name not provided, creating user in global context.") + end + db.execute("CREATE USER [#{@new_resource.username}] FOR LOGIN [#{@new_resource.username}]").do + @new_resource.updated_by_last_action(true) + end + ensure + close + end + end + + def action_drop + begin + if exists?(:users) + db.execute("DROP USER [#{@new_resource.username}]").do + @new_resource.updated_by_last_action(true) + end + if exists?(:logins) + db.execute("DROP LOGIN [#{@new_resource.username}]").do + @new_resource.updated_by_last_action(true) + end + ensure + close + end + end + + def action_grant + begin + if @new_resource.password + action_create + end + Chef::Application.fatal!('Please provide a database_name, SQL Server does not support global GRANT statements.') unless @new_resource.database_name + grant_statement = "GRANT #{@new_resource.privileges.join(', ')} ON DATABASE::[#{@new_resource.database_name}] TO [#{@new_resource.username}]" + Chef::Log.info("#{@new_resource} granting access with statement [#{grant_statement}]") + db.execute("USE [#{@new_resource.database_name}]").do + db.execute(grant_statement).do + @new_resource.updated_by_last_action(true) + ensure + close + end + end + + private + def exists?(type=:users) + case type + when :users + table = "database_principals" + if @new_resource.database_name + Chef::Log.debug("#{@new_resource} searching for existing user in '#{@new_resource.database_name}' database context.") + db.execute("USE [#{@new_resource.database_name}]").do + end + when :logins + table = "server_principals" + end + + result = db.execute("SELECT name FROM sys.#{table} WHERE name='#{@new_resource.username}'") + result.each.any? + end + end + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_database.rb b/chef/cookbooks/database/libraries/resource_database.rb new file mode 100644 index 0000000..103a999 --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_database.rb @@ -0,0 +1,119 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' + +class Chef + class Resource + class Database < Chef::Resource + + def initialize(name, run_context=nil) + super + @resource_name = :database + @database_name = name + @allowed_actions.push(:create, :drop, :query) + @action = :create + end + + def database_name(arg=nil) + set_or_return( + :database_name, + arg, + :kind_of => String + ) + end + + def connection(arg=nil) + set_or_return( + :connection, + arg, + :required => true + ) + end + + def sql(arg=nil, &block) + arg ||= block + set_or_return( + :sql, + arg, + :kind_of => [String, Proc] + ) + end + + def sql_query + if sql.kind_of?(Proc) + sql.call + else + sql + end + end + + def template(arg=nil) + set_or_return( + :template, + arg, + :kind_of => String, + :default => 'DEFAULT' + ) + end + + def collation(arg=nil) + set_or_return( + :collation, + arg, + :kind_of => String + ) + end + + def encoding(arg=nil) + set_or_return( + :encoding, + arg, + :kind_of => String, + :default => 'DEFAULT' + ) + end + + def tablespace(arg=nil) + set_or_return( + :tablespace, + arg, + :kind_of => String, + :default => 'DEFAULT' + ) + end + + def connection_limit(arg=nil) + set_or_return( + :connection_limit, + arg, + :kind_of => String, + :default => '-1' + ) + end + + def owner(arg=nil) + set_or_return( + :owner, + arg, + :kind_of => String + ) + end + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_database_user.rb b/chef/cookbooks/database/libraries/resource_database_user.rb new file mode 100644 index 0000000..043721a --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_database_user.rb @@ -0,0 +1,90 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database') + +class Chef + class Resource + class DatabaseUser < Chef::Resource::Database + + def initialize(name, run_context=nil) + super + @resource_name = :database_user + @username = name + + @database_name = nil + @table = nil + @host = 'localhost' + @privileges = [:all] + + @allowed_actions.push(:create, :drop, :grant) + @action = :create + end + + def database_name(arg=nil) + set_or_return( + :database_name, + arg, + :kind_of => String + ) + end + + def username(arg=nil) + set_or_return( + :username, + arg, + :kind_of => String + ) + end + + def password(arg=nil) + set_or_return( + :password, + arg, + :kind_of => String, + :required => true + ) + end + + def table(arg=nil) + set_or_return( + :table, + arg, + :kind_of => String + ) + end + + def host(arg=nil) + set_or_return( + :host, + arg, + :kind_of => String + ) + end + + def privileges(arg=nil) + set_or_return( + :privileges, + arg, + :kind_of => Array + ) + end + + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_mysql_database.rb b/chef/cookbooks/database/libraries/resource_mysql_database.rb new file mode 100644 index 0000000..726619e --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_mysql_database.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database') +require File.join(File.dirname(__FILE__), 'provider_database_mysql') + +class Chef + class Resource + class MysqlDatabase < Chef::Resource::Database + + def initialize(name, run_context=nil) + super + @resource_name = :mysql_database + @provider = Chef::Provider::Database::Mysql + end + + end + end +end \ No newline at end of file diff --git a/chef/cookbooks/database/libraries/resource_mysql_database_user.rb b/chef/cookbooks/database/libraries/resource_mysql_database_user.rb new file mode 100644 index 0000000..6e11ebe --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_mysql_database_user.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database_user') +require File.join(File.dirname(__FILE__), 'provider_database_mysql_user') + +class Chef + class Resource + class MysqlDatabaseUser < Chef::Resource::DatabaseUser + + def initialize(name, run_context=nil) + super + @resource_name = :mysql_database_user + @provider = Chef::Provider::Database::MysqlUser + end + + end + end +end \ No newline at end of file diff --git a/chef/cookbooks/database/libraries/resource_postgresql_database.rb b/chef/cookbooks/database/libraries/resource_postgresql_database.rb new file mode 100644 index 0000000..d8afb8c --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_postgresql_database.rb @@ -0,0 +1,35 @@ +# +# Author:: Seth Chisamore () +# Author:: Lamont Granquist () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database') +require File.join(File.dirname(__FILE__), 'provider_database_postgresql') + +class Chef + class Resource + class PostgresqlDatabase < Chef::Resource::Database + + def initialize(name, run_context=nil) + super + @resource_name = :postgresql_database + @provider = Chef::Provider::Database::Postgresql + end + + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb b/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb new file mode 100644 index 0000000..b3bf9d2 --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_postgresql_database_user.rb @@ -0,0 +1,35 @@ +# +# Author:: Seth Chisamore () +# Author:: Lamont Granquist () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database_user') +require File.join(File.dirname(__FILE__), 'provider_database_postgresql_user') + +class Chef + class Resource + class PostgresqlDatabaseUser < Chef::Resource::DatabaseUser + + def initialize(name, run_context=nil) + super + @resource_name = :postgresql_database_user + @provider = Chef::Provider::Database::PostgresqlUser + end + + end + end +end diff --git a/chef/cookbooks/database/libraries/resource_sql_server_database.rb b/chef/cookbooks/database/libraries/resource_sql_server_database.rb new file mode 100644 index 0000000..cdb17cb --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_sql_server_database.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database') +require File.join(File.dirname(__FILE__), 'provider_database_sql_server') + +class Chef + class Resource + class SqlServerDatabase < Chef::Resource::Database + + def initialize(name, run_context=nil) + super + @resource_name = :sql_server_database + @provider = Chef::Provider::Database::SqlServer + end + + end + end +end \ No newline at end of file diff --git a/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb b/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb new file mode 100644 index 0000000..56a3e03 --- /dev/null +++ b/chef/cookbooks/database/libraries/resource_sql_server_database_user.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.join(File.dirname(__FILE__), 'resource_database_user') +require File.join(File.dirname(__FILE__), 'provider_database_sql_server_user') + +class Chef + class Resource + class SqlServerDatabaseUser < Chef::Resource::DatabaseUser + + def initialize(name, run_context=nil) + super + @resource_name = :sql_server_database_user + @provider = Chef::Provider::Database::SqlServerUser + end + + end + end +end \ No newline at end of file diff --git a/chef/cookbooks/database/metadata.rb b/chef/cookbooks/database/metadata.rb new file mode 100644 index 0000000..e579288 --- /dev/null +++ b/chef/cookbooks/database/metadata.rb @@ -0,0 +1,22 @@ +name "database" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Sets up the database master or slave" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.4.1" + +recipe "database", "Empty placeholder" +recipe "database::ebs_backup", "Considered deprecated, older way of backing up EBS volumes" +recipe "database::ebs_volume", "Sets up an EBS volume in EC2 for the database" +recipe "database::master", "Creates application specific user and database" +recipe "database::snapshot", "Locks tables and freezes XFS filesystem for replication, assumes EC2 + EBS" + +depends "mysql", ">= 1.3.0" +depends "postgresql", ">= 1.0.0" +depends "aws" +depends "xfs" + +%w{ debian ubuntu centos suse fedora redhat scientific amazon }.each do |os| + supports os +end diff --git a/chef/cookbooks/database/recipes/default.rb b/chef/cookbooks/database/recipes/default.rb new file mode 100644 index 0000000..5d1819c --- /dev/null +++ b/chef/cookbooks/database/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: database +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/chef/cookbooks/database/recipes/ebs_backup.rb b/chef/cookbooks/database/recipes/ebs_backup.rb new file mode 100644 index 0000000..7a2f8a2 --- /dev/null +++ b/chef/cookbooks/database/recipes/ebs_backup.rb @@ -0,0 +1,89 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: database +# Recipe:: ebs_backup +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +begin + aws = Chef::DataBagItem.load('aws', 'main') + Chef::Log.info("Loaded AWS information from DataBagItem aws[#{aws['id']}]") +rescue + Chef::Log.fatal("Could not find the 'main' item in the 'aws' data bag") + raise +end + +db_role = String.new +db_master_role = String.new +db_type = node[:database][:type] + +search(:apps) do |app| + db_role = app["database_#{db_type}_role"] & node.run_list.roles + db_master_role = app["database_master_role"] +end + +ebs_info = Chef::DataBagItem.load(:aws, "ebs_#{db_master_role}_#{node.chef_environment}") + +gem_package "dbi" +gem_package "dbd-mysql" + +directory "/mnt/aws-config" do + mode 0700 + owner "root" + group "root" +end + +template "/mnt/aws-config/config" do + source "aws_config.erb" + variables( + :access_key => aws['aws_access_key_id'], + :secret_key => aws['aws_secret_access_key'] + ) + owner "root" + group "root" + mode 0600 +end + +git "/opt/ec2_mysql" do + repository "git://github.com/jtimberman/ec2_mysql.git" + reference "HEAD" + action :sync + not_if { ::FileTest.directory?("/opt/ec2_mysql/.git") } +end + +%w{backup restore}.each do |file| + template "/usr/local/bin/db-#{file}.sh" do + source "ebs-db-#{file}.sh.erb" + owner "root" + group "root" + mode 0700 + variables( + :mysql_root_passwd => node['mysql']['server_root_password'], + :mysql_device => node['mysql']['ebs_vol_dev'], + :ebs_vol_id => ebs_info['volume_id'] + ) + end +end + +if db_type == "master" && node.chef_environment == "production" + template "/etc/cron.d/db-backup" do + source "ebs-backup-cron.erb" + owner "root" + group "root" + mode 0644 + backup false + end +end diff --git a/chef/cookbooks/database/recipes/ebs_volume.rb b/chef/cookbooks/database/recipes/ebs_volume.rb new file mode 100644 index 0000000..0beea48 --- /dev/null +++ b/chef/cookbooks/database/recipes/ebs_volume.rb @@ -0,0 +1,204 @@ +# +# Author:: Joshua Timberman () +# Author:: AJ Christensen () +# Cookbook Name:: database +# Recipe:: ebs_volume +# +# Copyright 2009-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if node[:ec2] + include_recipe "aws" + include_recipe "xfs" + + begin + aws = Chef::DataBagItem.load(:aws, :main) + Chef::Log.info("Loaded AWS information from DataBagItem aws[#{aws['id']}]") + rescue + Chef::Log.fatal("Could not find the 'main' item in the 'aws' data bag") + raise + end + + ebs_vol_dev = node['mysql']['ebs_vol_dev'] + if (platform?("ubuntu") && node['platform_version'].to_f >= 11.04) + ebs_vol_dev_mount = ebs_vol_dev.sub(/^\/dev\/sd/, "/dev/xvd") + else + ebs_vol_dev_mount = ebs_vol_dev + end + ebs_vol_id = String.new + db_type = String.new + db_role = String.new + master_role = String.new + slave_role = String.new + root_pw = String.new + snapshots_to_keep = String.new + snapshot_cron_schedule = "00 * * * *" # default to hourly snapshots + + search(:apps) do |app| + if (app["database_master_role"] & node.run_list.roles).length == 1 || (app["database_slave_role"] & node.run_list.roles).length == 1 + master_role = app["database_master_role"] & node.run_list.roles + slave_role = app["database_slave_role"] & node.run_list.roles + root_pw = app["mysql_root_password"][node.chef_environment] + snapshots_to_keep = app["snapshots_to_keep"][node.chef_environment] + snapshot_cron_schedule = app["snapshot_cron_schedule"][node.chef_environment] if app["snapshot_cron_schedule"] && app["snapshot_cron_schedule"][node.chef_environment] + + if (master_role & node.run_list.roles).length == 1 + db_type = "master" + db_role = RUBY_VERSION.to_f <= 1.8 ? master_role : master_role.join + elsif (slave_role & node.run_list.roles).length == 1 + db_type = "slave" + db_role = RUBY_VERSION.to_f <= 1.8 ? slave_role : slave_role.join + end + + Chef::Log.info "database::ebs_volume - db_role: #{db_role} db_type: #{db_type}" + end + end + + begin + ebs_info = Chef::DataBagItem.load(:aws, "ebs_#{db_role}_#{node.chef_environment}") + Chef::Log.info("Loaded #{ebs_info['volume_id']} from DataBagItem aws[#{ebs_info['id']}]") + rescue + Chef::Log.warn("Could not find the 'ebs_#{db_role}_#{node.chef_environment}' item in the 'aws' data bag") + ebs_info = Hash.new + end + + begin + master_info = Chef::DataBagItem.load(:aws, "ebs_#{master_role}_#{node.chef_environment}") + Chef::Log.info "Loaded #{master_info['volume_id']} from DataBagItem aws[#{master_info['id']}]" + rescue + Chef::Application.fatal! "Could not load replication masters snapshot details", -41 if db_type == "slave" + end + + ruby_block "store_#{db_role}_#{node.chef_environment}_volid" do + block do + ebs_vol_id = node[:aws][:ebs_volume]["#{db_role}_#{node.chef_environment}"][:volume_id] + + unless ebs_info['volume_id'] + item = { + "id" => "ebs_#{db_role}_#{node.chef_environment}", + "volume_id" => ebs_vol_id + } + Chef::Log.info "Storing volume_id #{item.inspect}" + databag_item = Chef::DataBagItem.new + databag_item.data_bag("aws") + databag_item.raw_data = item + databag_item.save + Chef::Log.info("Created #{item['id']} in #{databag_item.data_bag}") + end + end + action :nothing + end + + aws_ebs_volume "#{db_role}_#{node.chef_environment}" do + aws_access_key aws['aws_access_key_id'] + aws_secret_access_key aws['aws_secret_access_key'] + size 50 + device ebs_vol_dev + snapshots_to_keep snapshots_to_keep + case db_type + when "master" + if ebs_info['volume_id'] && ebs_info['volume_id'] =~ /vol/ + volume_id ebs_info['volume_id'] + action :attach + elsif ebs_info['volume_id'] && ebs_info['volume_id'] =~ /snap/ + snapshot_id ebs_info['volume_id'] + action [ :create, :attach ] + else + action [ :create, :attach ] + end + notifies :create, resources(:ruby_block => "store_#{db_role}_#{node.chef_environment}_volid") + when "slave" + if master_info['volume_id'] + snapshot_id master_info['volume_id'] + action [:create, :attach] + else + Chef::Log.warn("Couldn't detect snapshot ID.") + action :nothing + end + end + provider "aws_ebs_volume" + end + + if db_type == "master" + Chef::Log.info "Setting up templates for chef-solo snapshots" + template "/etc/chef/chef-solo-database-snapshot.rb" do + source "chef-solo-database-snapshot.rb.erb" + variables :cookbook_path => Chef::Config[:cookbook_path] + owner "root" + group "root" + mode 0600 + end + + template "/etc/chef/chef-solo-database-snapshot.json" do + source "chef-solo-database-snapshot.json.erb" + variables( + :output => { + 'db_snapshot' => { + 'ebs_vol_dev' => node.mysql.ec2_path, + 'db_role' => db_role, + 'app_environment' => node.chef_environment, + 'username' => 'root', + 'password' => root_pw, + 'aws_access_key_id' => aws['aws_access_key_id'], + 'aws_secret_access_key' => aws['aws_secret_access_key'], + 'snapshots_to_keep' => snapshots_to_keep, + 'volume_id' => ebs_info['volume_id'] + }, + 'run_list' => [ + "recipe[database::snapshot]" + ] + } + ) + owner "root" + group "root" + mode 0600 + end + + template "/etc/cron.d/chef-solo-database-snapshot" do + source "chef-solo-database-snapshot.cron.erb" + variables( + :json_attribs => "/etc/chef/chef-solo-database-snapshot.json", + :config_file => "/etc/chef/chef-solo-database-snapshot.rb", + :schedule => snapshot_cron_schedule + ) + owner "root" + group "root" + mode 0600 + end + end + + execute "mkfs.xfs #{ebs_vol_dev_mount}" do + only_if "xfs_admin -l #{ebs_vol_dev_mount} 2>&1 | grep -qx 'xfs_admin: #{ebs_vol_dev_mount} is not a valid XFS filesystem (unexpected SB magic number 0x00000000)'" + end + + %w{ec2_path data_dir}.each do |dir| + directory node['mysql'][dir] do + mode 0755 + end + end + + mount node['mysql']['ec2_path'] do + device ebs_vol_dev_mount + fstype "xfs" + action :mount + end + + mount node['mysql']['data_dir'] do + device node['mysql']['ec2_path'] + fstype "none" + options "bind,rw" + action :mount + end +end diff --git a/chef/cookbooks/database/recipes/master.rb b/chef/cookbooks/database/recipes/master.rb new file mode 100644 index 0000000..166df94 --- /dev/null +++ b/chef/cookbooks/database/recipes/master.rb @@ -0,0 +1,78 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: database +# Recipe:: master +# +# Copyright 2009-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This is potentially destructive to the nodes mysql password attributes, since +# we iterate over all the app databags. If this database server provides +# databases for multiple applications, the last app found in the databags +# will win out, so make sure the databags have the same passwords set for +# the root, repl, and debian-sys-maint users. +# + +db_info = Hash.new +root_pw = String.new + +search(:apps) do |app| + (app['database_master_role'] & node.run_list.roles).each do |dbm_role| + %w{ root repl debian }.each do |user| + user_pw = app["mysql_#{user}_password"] + if !user_pw.nil? and user_pw[node.chef_environment] + Chef::Log.debug("Saving password for #{user} as node attribute node['mysql']['server_#{user}_password'") + node.set['mysql']["server_#{user}_password"] = user_pw[node.chef_environment] + node.save + else + log "A password for MySQL user #{user} was not found in DataBag 'apps' item '#{app["id"]}' for environment ' for #{node.chef_environment}'." do + level :warn + end + log "A random password will be generated by the mysql cookbook and added as 'node.mysql.server_#{user}_password'. Edit the DataBag item to ensure it is set correctly on new nodes" do + level :warn + end + end + end + app['databases'].each do |env,db| + db_info[env] = db + end + end +end + +include_recipe "mysql::server" + +connection_info = {:host => "localhost", :username => 'root', :password => node['mysql']['server_root_password']} + +search(:apps) do |app| + (app['database_master_role'] & node.run_list.roles).each do |dbm_role| + app['databases'].each do |env,db| + if env =~ /#{node.chef_environment}/ + mysql_database "create #{db['database']}" do + database_name db['database'] + connection connection_info + action :create + end + %W{ % #{node['fqdn']} localhost }.each do |h| + mysql_database_user db['username'] do + connection connection_info + password db['password'] + database_name db['database'] + host h + action :grant + end + end + end + end + end +end diff --git a/chef/cookbooks/database/recipes/mysql.rb b/chef/cookbooks/database/recipes/mysql.rb new file mode 100644 index 0000000..8b83420 --- /dev/null +++ b/chef/cookbooks/database/recipes/mysql.rb @@ -0,0 +1,20 @@ +# +# Author:: Jesse Howarth () +# +# Copyright:: Copyright (c) 2012, Opscode, Inc. () +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "mysql::ruby" diff --git a/chef/cookbooks/database/recipes/postgresql.rb b/chef/cookbooks/database/recipes/postgresql.rb new file mode 100644 index 0000000..c4ab005 --- /dev/null +++ b/chef/cookbooks/database/recipes/postgresql.rb @@ -0,0 +1,20 @@ +# +# Author:: Jesse Howarth () +# +# Copyright:: Copyright (c) 2012, Opscode, Inc. () +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::ruby" diff --git a/chef/cookbooks/database/recipes/snapshot.rb b/chef/cookbooks/database/recipes/snapshot.rb new file mode 100644 index 0000000..fe2a4f4 --- /dev/null +++ b/chef/cookbooks/database/recipes/snapshot.rb @@ -0,0 +1,62 @@ +# +# Author:: AJ Christensen () +# Cookbook Name:: database +# Recipe:: snapshot +# +# Copyright 2009-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "aws" +include_recipe "xfs" + +%w{ebs_vol_dev db_role app_environment username password aws_access_key_id aws_secret_access_key snapshots_to_keep volume_id}.collect do |key| + Chef::Application.fatal!("Required db_snapshot configuration #{key} not found.", -47) unless node.db_snapshot.has_key? key +end + +connection_info = {:host => "localhost", :username => node.db_snapshot.username, :password => node.db_snapshot.password} + +mysql_database "locking tables for #{node.db_snapshot.app_environment}" do + connection connection_info + sql "flush tables with read lock" + action :query +end + +execute "xfs freeze" do + command "xfs_freeze -f #{node.db_snapshot.ebs_vol_dev}" +end + +aws_ebs_volume "#{node.db_snapshot.db_role.first}_#{node.db_snapshot.app_environment}" do + aws_access_key node.db_snapshot.aws_access_key_id + aws_secret_access_key node.db_snapshot.aws_secret_access_key + size 50 + device node.db_snapshot.ebs_vol_dev + snapshots_to_keep node.db_snapshot.snapshots_to_keep + action :snapshot + volume_id node.db_snapshot.volume_id + ignore_failure true # if this fails, continue to unfreeze and unlock +end + +execute "xfs unfreeze" do + command "xfs_freeze -u #{node.db_snapshot.ebs_vol_dev}" +end + +mysql_database "unflushing tables for #{node.db_snapshot.app_environment}" do + connection connection_info + sql "unlock tables" + action :query +end + +aws_ebs_volume "#{node.db_snapshot.db_role.first}_#{node.db_snapshot.app_environment}" do + action :prune +end diff --git a/chef/cookbooks/database/templates/default/app_grants.sql.erb b/chef/cookbooks/database/templates/default/app_grants.sql.erb new file mode 100644 index 0000000..35d8b98 --- /dev/null +++ b/chef/cookbooks/database/templates/default/app_grants.sql.erb @@ -0,0 +1,8 @@ +# Generated by Chef. Local modifications will be overwritten. +<% @db_info.each do |env,db| -%> +# Privileges for databases in <%= env %> +GRANT ALL ON <%= db['database'] %>.* TO '<%= db['username'] %>'@'localhost' IDENTIFIED BY '<%= db['password'] %>'; +GRANT ALL ON <%= db['database'] %>.* TO '<%= db['username'] %>'@'<%= node['fqdn'] %>' IDENTIFIED BY '<%= db['password'] %>'; +GRANT ALL ON <%= db['database'] %>.* TO '<%= db['username'] %>'@'%' IDENTIFIED BY '<%= db['password'] %>'; +<% end -%> +flush privileges; diff --git a/chef/cookbooks/database/templates/default/aws_config.erb b/chef/cookbooks/database/templates/default/aws_config.erb new file mode 100644 index 0000000..e5ae368 --- /dev/null +++ b/chef/cookbooks/database/templates/default/aws_config.erb @@ -0,0 +1,3 @@ +AWS_ACCESS_KEY_ID=<%= @access_key %> +AWS_SECRET_ACCESS_KEY=<%= @secret_key %> +BUCKET_BASE_NAME=db-backups diff --git a/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.cron.erb b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.cron.erb new file mode 100644 index 0000000..db40768 --- /dev/null +++ b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.cron.erb @@ -0,0 +1,6 @@ +# Managed by Chef +# m h dom mon dow command +# Keep 1 day of hourly snapshots +PATH=/usr/sbin:/usr/bin:/sbin:/bin +<% cs = "chef-solo -j #{@json_attribs} -c #{@config_file}" %> +<%= @schedule %> root <%= cs %> diff --git a/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.json.erb b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.json.erb new file mode 100644 index 0000000..13cdb64 --- /dev/null +++ b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.json.erb @@ -0,0 +1 @@ +<%= require 'json'; JSON.pretty_generate(@output) %> diff --git a/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.rb.erb b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.rb.erb new file mode 100644 index 0000000..12806cf --- /dev/null +++ b/chef/cookbooks/database/templates/default/chef-solo-database-snapshot.rb.erb @@ -0,0 +1,6 @@ +executable_path ENV['PATH'] ? ENV['PATH'].split(File::PATH_SEPARATOR) : [] +<% if @cookbook_path.is_a? Array %> + cookbook_path [ <%= @cookbook_path.collect { |cb| "\"#{cb}\""}.join(", ") -%> ] +<% else %> + cookbook_path "<%= @cookbook_path -%>" +<% end %> diff --git a/chef/cookbooks/database/templates/default/ebs-backup-cron.erb b/chef/cookbooks/database/templates/default/ebs-backup-cron.erb new file mode 100644 index 0000000..9293fdf --- /dev/null +++ b/chef/cookbooks/database/templates/default/ebs-backup-cron.erb @@ -0,0 +1,2 @@ +# Chef Name: ebs_db_backup +15 0 * * * root /usr/local/bin/db-backup.sh diff --git a/chef/cookbooks/database/templates/default/ebs-db-backup.sh.erb b/chef/cookbooks/database/templates/default/ebs-db-backup.sh.erb new file mode 100644 index 0000000..60e1c91 --- /dev/null +++ b/chef/cookbooks/database/templates/default/ebs-db-backup.sh.erb @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Back up a MySQL database via EBS snapshot + +. /mnt/aws-config/config + +/opt/ec2_mysql/bin/ec2_mysql -a $AWS_ACCESS_KEY_ID -s $AWS_SECRET_ACCESS_KEY -p '<%= @mysql_root_passwd %>' -k 5 master +echo "done" diff --git a/chef/cookbooks/database/templates/default/ebs-db-restore.sh.erb b/chef/cookbooks/database/templates/default/ebs-db-restore.sh.erb new file mode 100644 index 0000000..47afef6 --- /dev/null +++ b/chef/cookbooks/database/templates/default/ebs-db-restore.sh.erb @@ -0,0 +1,10 @@ +#!/bin/bash +# +# Restore a MySQL database from EBS + +mkdir -p /mnt/restore + +. /mnt/aws-config/config + +/opt/ec2_mysql/bin/ec2_mysql -a $AWS_ACCESS_KEY_ID -s $AWS_SECRET_ACCESS_KEY -p '<%= @mysql_root_password %>' -v '<%= @ebs_vol_id %>' -m /mnt/restore -d <%= @mysql_device %> -r <%= @mysql_device %> -l debug -n slave +echo "done" diff --git a/chef/cookbooks/database/templates/default/s3cfg.erb b/chef/cookbooks/database/templates/default/s3cfg.erb new file mode 100644 index 0000000..c2f818c --- /dev/null +++ b/chef/cookbooks/database/templates/default/s3cfg.erb @@ -0,0 +1,27 @@ +[default] +access_key = <%= @aws['aws_access_key_id'] %> +acl_public = False +bucket_location = US +debug_syncmatch = False +default_mime_type = binary/octet-stream +delete_removed = False +dry_run = False +encrypt = False +force = False +gpg_command = /usr/bin/gpg +gpg_decrypt = %(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s +gpg_encrypt = %(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s +gpg_passphrase = +guess_mime_type = False +host_base = s3.amazonaws.com +host_bucket = %(bucket)s.s3.amazonaws.com +human_readable_sizes = False +preserve_attrs = True +proxy_host = +proxy_port = 0 +recv_chunk = 4096 +secret_key = <%= @aws['aws_secret_access_key'] %> +send_chunk = 4096 +simpledb_host = sdb.amazonaws.com +use_https = True +verbosity = WARNING diff --git a/chef/cookbooks/dmg/CHANGELOG.md b/chef/cookbooks/dmg/CHANGELOG.md new file mode 100644 index 0000000..ecaba9d --- /dev/null +++ b/chef/cookbooks/dmg/CHANGELOG.md @@ -0,0 +1,27 @@ +dmg Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the dmg ookbook. + + +v2.0.0 +------ +### Bug +- **[COOK-3389](https://tickets.opscode.com/browse/COOK-3389)** - Use `rsync` instead of `cp` (potentially a breaking change on some systems) + +v1.1.0 +------ +- [COOK-1847] - accept owner parameter for installing packages + +v1.0.0 +------ +- [COOK-852] - Support "pkg" in addition to "mpkg" package types + +v0.7.0 +------ +- [COOK-854] - use `cp -R` instead of `cp -r` +- [COOK-855] - specify a file or directory to check for prior install + +v0.6.0 +------ +- option to install software that is an .mpkg inside a .dmg +- ignore failure on chmod in case mode is already set, or is root owned diff --git a/chef/cookbooks/dmg/CONTRIBUTING b/chef/cookbooks/dmg/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/dmg/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/dmg/LICENSE b/chef/cookbooks/dmg/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/dmg/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/dmg/README.md b/chef/cookbooks/dmg/README.md new file mode 100644 index 0000000..cbf74db --- /dev/null +++ b/chef/cookbooks/dmg/README.md @@ -0,0 +1,142 @@ +Description +=========== + +Lightweight resource and provider to install OS X applications (.app) from dmg files. + +Requirements +============ + +## Platform: + +* Mac OS X + +Resources and Providers +======================= + +dmg\_package +------------ + +This resource will install a DMG "Package". It will retrieve the DMG from a remote URL, mount it using OS X's `hdid`, copy the application (.app directory) to the specified destination (/Applications), and detach the image using `hdiutil`. The dmg file will be stored in the `Chef::Config[:file_cache_path]`. If you want to install an application that has already been downloaded (not using the `source` parameter), copy it to the appropriate location. You can find out what directory this is with the following command on the node to run chef: + + knife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb + +Optionally, the LWRP can install an "mpkg" or "pkg" package using installer(8). + +# Actions: + +* :install - Installs the application. + +# Parameter attributes: + +* `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications. +* `source` - remote URL for the dmg to download if specified. Default is nil. +* `owner` - owner that should own the package installation. +* `destination` - directory to copy the .app into. Default is /Applications. +* `checksum` - sha256 checksum of the dmg to download. Default is nil. +* `type` - type of package, "app", "pkg" or "mpkg". Default is "app". When using "pkg" or "mpkg", the destination must be /Applications. +* `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute. +* `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed +* `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces. +* `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting. +* `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false. + +Usage Examples +============== + +Install `/Applications/Tunnelblick.app` from the primary download site. + + dmg_package "Tunnelblick" do + source "http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg" + checksum "a3fae60b6833175f32df20c90cd3a3603a" + action :install + end + +Install Google Chrome. Uses the `dmg_name` because the application name has spaces. Installs in `/Applications/Google Chrome.app`. + + dmg_package "Google Chrome" do + dmg_name "googlechrome" + source "https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg" + checksum "7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a" + action :install + end + +Install Dropbox. Uses `volumes_dir` because the mounted directory is different than the name of the application directory. Installs in `/Applications/Dropbox.app`. + + dmg_package "Dropbox" do + volumes_dir "Dropbox Installer" + source "http://www.dropbox.com/download?plat=mac" + checksum "b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13" + action :install + end + +Install MacIrssi to `~/Applications` from the local file downloaded to the cache path into an Applications directory in the current user's home directory. Chef should run as a non-root user for this. + + directory "#{ENV['HOME']}/Applications" + + dmg_package "MacIrssi" do + destination "#{ENV['HOME']}/Applications" + action :install + end + +Install Virtualbox to `/Applications` from the .mpkg: + + dmg_package "Virtualbox" do + source "http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg" + type "mpkg" + end + +Install pgAdmin to `/Applications` and automatically accept the EULA: + + dmg_package "pgAdmin3" do + source "http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg" + checksum "9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9" + accept_eula true + end + +Install Pivotal Tracker to `/Applications` using a password-protected dmg: + + dmg_package "Pivotal Tracker" do + volumes_dir "tracker" + source "http://cheffiles.pivotallabs.com/fluid_tracker.dmg" + dmg_passphrase "xyz" + end + +Install Silverlight, with idempotence check based on pkgutil: + + dmg_package "Silerlight" do + source "http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg" + type "pkg" + checksum "6d4a0ad4552d9815531463eb3f467fb8cf4bffcc" + package_id "com.microsoft.installSilverlightPlugin" + end + +To do +===== + +A few things remain outstanding to make this cookbook "1.0" quality. + +* support downloading a .dmg.zip and unzipping it +* specify a local .dmg already downloaded in another location (like ~/Downloads) + +Some things that would be nice to have at some point. + +* use hdiutil to mount/attach the disk image +* automatically detect the `volumes_dir` where the image is attached +* be able to automatically accept license agreements + +License and Author +================== + +* Copyright 2011, Joshua Timberman + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/dmg/attributes/default.rb b/chef/cookbooks/dmg/attributes/default.rb new file mode 100644 index 0000000..4935ec8 --- /dev/null +++ b/chef/cookbooks/dmg/attributes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: dmg +# Attributes:: default +# +# Copyright 2011, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +default[:dmg][:base_dir] = "/Applications" +default[:dmg][:cache_dir] = Chef::Config[:file_cache_path] diff --git a/chef/cookbooks/dmg/metadata.rb b/chef/cookbooks/dmg/metadata.rb new file mode 100644 index 0000000..0738cf4 --- /dev/null +++ b/chef/cookbooks/dmg/metadata.rb @@ -0,0 +1,8 @@ +name "dmg" +maintainer "Joshua Timberman" +maintainer_email "cookbooks@housepub.org" +license "Apache 2.0" +description "LWRP to install OS X applications from dmgs" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "2.0.1" +supports "mac_os_x" diff --git a/chef/cookbooks/dmg/providers/package.rb b/chef/cookbooks/dmg/providers/package.rb new file mode 100644 index 0000000..7b36d7b --- /dev/null +++ b/chef/cookbooks/dmg/providers/package.rb @@ -0,0 +1,82 @@ +# +# Cookbook Name:: dmg +# Provider:: package +# +# Copyright 2011, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def load_current_resource + @dmgpkg = Chef::Resource::DmgPackage.new(new_resource.name) + @dmgpkg.app(new_resource.app) + Chef::Log.debug("Checking for application #{new_resource.app}") + @dmgpkg.installed(installed?) +end + +action :install do + unless @dmgpkg.installed + + volumes_dir = new_resource.volumes_dir ? new_resource.volumes_dir : new_resource.app + dmg_name = new_resource.dmg_name ? new_resource.dmg_name : new_resource.app + dmg_file = "#{Chef::Config[:file_cache_path]}/#{dmg_name}.dmg" + + remote_file "#{dmg_file} - #{@dmgpkg.name}" do + path dmg_file + source new_resource.source + checksum new_resource.checksum if new_resource.checksum + only_if { new_resource.source } + end + + passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : "" + ruby_block "attach #{dmg_file}" do + block do + software_license_agreement = system("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'") + raise "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula + accept_eula_cmd = new_resource.accept_eula ? "echo Y |" : "" + system "#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}'" + end + not_if "hdiutil info #{passphrase_cmd} | grep -q 'image-path.*#{dmg_file}'" + end + + case new_resource.type + when "app" + execute "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'" do + user new_resource.owner if new_resource.owner + end + + file "#{new_resource.destination}/#{new_resource.app}.app/Contents/MacOS/#{new_resource.app}" do + mode 0755 + ignore_failure true + end + when "mpkg", "pkg" + execute "sudo installer -pkg '/Volumes/#{volumes_dir}/#{new_resource.app}.#{new_resource.type}' -target /" + end + + execute "hdiutil detach '/Volumes/#{volumes_dir}'" + end +end + +private + +def installed? + if ( ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app") ) + Chef::Log.info "Already installed; to upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\"" + true + elsif ( system("pkgutil --pkgs=#{new_resource.package_id}") ) + Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget #{new_resource.package_id}\"" + true + else + false + end +end diff --git a/chef/cookbooks/dmg/recipes/default.rb b/chef/cookbooks/dmg/recipes/default.rb new file mode 100644 index 0000000..6fcb02a --- /dev/null +++ b/chef/cookbooks/dmg/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: dmg +# Recipe:: default +# +# Copyright 2011, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/dmg/resources/package.rb b/chef/cookbooks/dmg/resources/package.rb new file mode 100644 index 0000000..3c83681 --- /dev/null +++ b/chef/cookbooks/dmg/resources/package.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: dmg +# Resource:: package +# +# Copyright 2011, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +actions :install + +attribute :app, :kind_of => String, :name_attribute => true +attribute :source, :kind_of => String, :default => nil +attribute :owner, :kind_of => String, :default => nil +attribute :destination, :kind_of => String, :default => "/Applications" +attribute :checksum, :kind_of => String, :default => nil +attribute :volumes_dir, :kind_of => String, :default => nil +attribute :dmg_name, :kind_of => String, :default => nil +attribute :type, :kind_of => String, :default => "app" +attribute :installed, :kind_of => [TrueClass, FalseClass], :default => false +attribute :package_id, :kind_of => String, :default => nil +attribute :dmg_passphrase, :kind_of => String, :default => nil +attribute :accept_eula, :kind_of => [TrueClass, FalseClass], :default => false + +def initialize(name, run_context=nil) + super + @action = :install +end diff --git a/chef/cookbooks/erlang/.kitchen.yml b/chef/cookbooks/erlang/.kitchen.yml new file mode 100644 index 0000000..bb261dd --- /dev/null +++ b/chef/cookbooks/erlang/.kitchen.yml @@ -0,0 +1,54 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: + - "recipe[minitest-handler]" + - "recipe[erlang]" + attributes: {} + +- name: gui_tools + run_list: + - "recipe[minitest-handler]" + - "recipe[erlang]" + attributes: {erlang: {gui_tools: true}} + excludes: ["centos-5.9", "centos-6.4"] + +- name: esl + run_list: + - "recipe[minitest-handler]" + - "recipe[erlang::esl]" + attributes: {} + excludes: ["centos-5.9", "centos-6.4"] + +- name: source + run_list: + - "recipe[minitest-handler]" + - "recipe[erlang::source]" + attributes: {} diff --git a/chef/cookbooks/erlang/Berksfile b/chef/cookbooks/erlang/Berksfile new file mode 100644 index 0000000..f2f9d86 --- /dev/null +++ b/chef/cookbooks/erlang/Berksfile @@ -0,0 +1,8 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" + cookbook "minitest-handler" +end diff --git a/chef/cookbooks/erlang/CHANGELOG.md b/chef/cookbooks/erlang/CHANGELOG.md new file mode 100644 index 0000000..ffe1d5c --- /dev/null +++ b/chef/cookbooks/erlang/CHANGELOG.md @@ -0,0 +1,37 @@ +erland Cookbook CHANGELOG +========================= +This file is used to list changes made in each version of the erlang cookbook. + + +v1.3.2 +------ +### New Feature +- **[COOK-2915](https://tickets.opscode.com/browse/COOK-2915)** - Debian codename override + +v1.3.0 +------ +- Add support for Test Kitchen 1.0 + +### Bug +- [COOK-2595]: erlang cookbook now incorrectly depends on apt <= 1.7.0 +- [COOK-2894]: erlang::esl uses nil attribute + +### Improvement +- [COOK-2509]: Add support for installing Erlang/OTP from Erlang Solutions' repositories + +v1.2.0 +------ +- [COOK-2287] - Add support for installing Erlang from source + +v1.1.2 +------ +- [COOK-1215] - Support Amazon Linux in erlang cookbook +- [COOK-1884] - Erlang Readme does not reflect cookbook requirements + +v1.1.0 +------ +- [COOK-1782] - Add test kitchen support + +v1.0.0 +------ +- [COOK-905] - Fix installation on RHEL/CentOS 6+ diff --git a/chef/cookbooks/erlang/CONTRIBUTING.md b/chef/cookbooks/erlang/CONTRIBUTING.md new file mode 100644 index 0000000..3a99897 --- /dev/null +++ b/chef/cookbooks/erlang/CONTRIBUTING.md @@ -0,0 +1,257 @@ +# Contributing to Opscode Cookbooks + +We are glad you want to contribute to Opscode Cookbooks! The first +step is the desire to improve the project. + +You can find the answers to additional frequently asked questions +[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). + +You can find additional information about +[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) +on the wiki as well. + +## Quick-contribute + +* Create an account on our [bug tracker](http://tickets.opscode.com) +* Sign our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) +(keep reading if you're contributing on behalf of your employer) +* Create a ticket for your change on the + [bug tracker](http://tickets.opscode.com) +* Link to your patch as a rebased git branch or pull request from the + ticket +* Resolve the ticket as fixed + +We regularly review contributions and will get back to you if we have +any suggestions or concerns. + +## The Apache License and the CLA/CCLA + +Licensing is very important to open source projects, it helps ensure +the software continues to be available under the terms that the author +desired. Chef uses the Apache 2.0 license to strike a balance between +open contribution and allowing you to use the software however you +would like to. + +The license tells you what rights you have that are provided by the +copyright holder. It is important that the contributor fully +understands what rights they are licensing and agrees to them. +Sometimes the copyright holder isn't the contributor, most often when +the contributor is doing work for a company. + +To make a good faith effort to ensure these criteria are met, Opscode +requires a Contributor License Agreement (CLA) or a Corporate +Contributor License Agreement (CCLA) for all contributions. This is +without exception due to some matters not being related to copyright +and to avoid having to continually check with our lawyers about small +patches. + +It only takes a few minutes to complete a CLA, and you retain the +copyright to your contribution. + +You can complete our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). +If you're contributing on behalf of your employer, have your employer +fill out our +[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) +instead. + +## Ticket Tracker (JIRA) + +The [ticket tracker](http://tickets.opscode.com) is the most important +documentation for the code base. It provides significant historical +information, such as: + +* Which release a bug fix is included in +* Discussion regarding the design and merits of features +* Error output to aid in finding similar bugs + +Each ticket should aim to fix one bug or add one feature. + +## Using git + +You can get a quick copy of the repository for this cookbook by +running `git clone +git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. + +For collaboration purposes, it is best if you create a Github account +and fork the repository to your own account. Once you do this you will +be able to push your changes to your Github repository for others to +see and use. + +If you have another repository in your GitHub account named the same +as the cookbook, we suggest you suffix the repository with -cookbook. + +### Branches and Commits + +You should submit your patch as a git branch named after the ticket, +such as COOK-1337. This is called a _topic branch_ and allows users to +associate a branch of code with the ticket. + +It is a best practice to have your commit message have a _summary +line_ that includes the ticket number, followed by an empty line and +then a brief description of the commit. This also helps other +contributors understand the purpose of changes to the code. + + [COOK-1757] - platform_family and style + + * use platform_family for platform checking + * update notifies syntax to "resource_type[resource_name]" instead of + resources() lookup + * COOK-692 - delete config files dropped off by packages in conf.d + * dropped debian 4 support because all other platforms have the same + values, and it is older than "old stable" debian release + +Remember that not all users use Chef in the same way or on the same +operating systems as you, so it is helpful to be clear about your use +case and change so they can understand it even when it doesn't apply +to them. + +### Github and Pull Requests + +All of Opscode's open source cookbook projects are available on +[Github](http://www.github.com/opscode-cookbooks). + +We don't require you to use Github, and we will even take patch diffs +attached to tickets on the tracker. However Github has a lot of +convenient features, such as being able to see a diff of changes +between a pull request and the main repository quickly without +downloading the branch. + +If you do choose to use a pull request, please provide a link to the +pull request from the ticket __and__ a link to the ticket from the +pull request. Because pull requests only have two states, open and +closed, we can't easily filter pull requests that are waiting for a +reply from the author for various reasons. + +### More information + +Additional help with git is available on the +[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) +wiki page. + +## Functional and Unit Tests + +This cookbook is set up to run tests under +[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It +uses minitest-chef to run integration tests after the node has been +converged to verify that the state of the node. + +Test kitchen should run completely without exception using the default +[baseboxes provided by Opscode](https://github.com/opscode/bento). +Because Test Kitchen creates VirtualBox machines and runs through +every configuration in the Kitchenfile, it may take some time for +these tests to complete. + +If your changes are only for a specific recipe, run only its +configuration with Test Kitchen. If you are adding a new recipe, or +other functionality such as a LWRP or definition, please add +appropriate tests and ensure they run with Test Kitchen. + +If any don't pass, investigate them before submitting your patch. + +Any new feature should have unit tests included with the patch with +good code coverage to help protect it from future changes. Similarly, +patches that fix a bug or regression should have a _regression test_. +Simply put, this is a test that would fail without your patch but +passes with it. The goal is to ensure this bug doesn't regress in the +future. Consider a regular expression that doesn't match a certain +pattern that it should, so you provide a patch and a test to ensure +that the part of the code that uses this regular expression works as +expected. Later another contributor may modify this regular expression +in a way that breaks your use cases. The test you wrote will fail, +signalling to them to research your ticket and use case and accounting +for it. + +If you need help writing tests, please ask on the Chef Developer's +mailing list, or the #chef-hacking IRC channel. + +## Code Review + +Opscode regularly reviews code contributions and provides suggestions +for improvement in the code itself or the implementation. + +We find contributions by searching the ticket tracker for _resolved_ +tickets with a status of _fixed_. If we have feedback we will reopen +the ticket and you should resolve it again when you've made the +changes or have a response to our feedback. When we believe the patch +is ready to be merged, we will tag the _Code Reviewed_ field with +_Reviewed_. + +Depending on the project, these tickets are then merged within a week +or two, depending on the current release cycle. + +## Release Cycle + +The versioning for Opscode Cookbook projects is X.Y.Z. + +* X is a major release, which may not be fully compatible with prior + major releases +* Y is a minor release, which adds both new features and bug fixes +* Z is a patch release, which adds just bug fixes + +A released version of a cookbook will end in an even number, e.g. +"1.2.4" or "0.8.0". When development for the next version of the +cookbook begins, the "Z" patch number is incremented to the next odd +number, however the next release of the cookbook may be a major or +minor incrementing version. + +Releases of Opscode's cookbooks are usually announced on the Chef user +mailing list. Releases of several cookbooks may be batched together +and announced on the [Opscode Blog](http://www.opscode.com/blog). + +## Working with the community + +These resources will help you learn more about Chef and connect to +other members of the Chef community: + +* [chef](http://lists.opscode.com/sympa/info/chef) and + [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing + lists +* #chef and #chef-hacking IRC channels on irc.freenode.net +* [Community Cookbook site](http://community.opscode.com) +* [Chef wiki](http://wiki.opscode.com/display/chef) +* Opscode Chef [product page](http://www.opscode.com/chef) + + +## Cookbook Contribution Do's and Don't's + +Please do include tests for your contribution. If you need help, ask +on the +[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) +or the +[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). +Not all platforms that a cookbook supports may be supported by Test +Kitchen. Please provide evidence of testing your contribution if it +isn't trivial so we don't have to duplicate effort in testing. Chef +10.14+ "doc" formatted output is sufficient. + +Please do indicate new platform (families) or platform versions in the +commit message, and update the relevant ticket. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] - Updated pool resource to correctly + delete.' + +Please do use [foodcritic](http://acrmp.github.com/foodcritic) to +lint-check the cookbook. Except FC007, it should pass all correctness +rules. FC007 is okay as long as the dependent cookbooks are *required* +for the default behavior of the cookbook, such as to support an +uncommon platform, secondary recipe, etc. + +Please do ensure that your changes do not break or modify behavior for +other platforms supported by the cookbook. For example if your changes +are for Debian, make sure that they do not break on CentOS. + +Please do not modify the version number in the metadata.rb, Opscode +will select the appropriate version based on the release cycle +information above. + +Please do not update the CHANGELOG.md for a new version. Not all +changes to a cookbook may be merged and released in the same versions. +Opscode will update the CHANGELOG.md when releasing a new version of +the cookbook. diff --git a/chef/cookbooks/erlang/LICENSE b/chef/cookbooks/erlang/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/erlang/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/erlang/README.md b/chef/cookbooks/erlang/README.md new file mode 100644 index 0000000..2a417ad --- /dev/null +++ b/chef/cookbooks/erlang/README.md @@ -0,0 +1,90 @@ +Description +=========== + +Manages installation of Erlang via packages or source. + +Requirements +============ + +## Chef + +Chef version 0.10.10+ and Ohai 0.6.12+ are required + +## Platform + +Tested on: + +* Ubuntu 10.04, 11.10, 12.04 +* Red Hat Enterprise Linux (CentOS/Amazon/Scientific/Oracle) 5.7, 6.2 + +**Notes**: This cookbook has been tested on the listed platforms. It + may work on other platforms with or without modification. + +## Cookbooks + +* yum (for epel recipe) +* build-essential (for source compilation) + +Attributes +========== + +* `node['erlang']['gui_tools']` - whether to install the GUI tools for + Erlang. +* `node['erlang']['install_method']` - Erlang installation method + ("package", "source", or "esl" (for Erlang Solutions packages)). +* `node['erlang']['source']['version']` - Version of Erlang/OTP to install from source. + "source") +* `node['erlang']['source']['url']` - URL of Erlang/OTP source tarball. +* `node['erlang']['source']['checksum']` - Checksum of the Erlang/OTP source tarball. +* `node['erlang']['esl']['version']` - version specifier for Erlang + Solutions packages. +* `node['erlang']['esl']['lsb_codename']` - override the code name + used for ESL packages, useful for installing the packages on + distributions that they don't make specific packages available + (e.g., maverick vs precise). + +Recipes +======= + +## default + +Manages installation of Erlang. Includes the package or source recipe +depending on the value of `node['erlang']['install_method']`. + +## package + +Installs Erlang from distribution packages. + +## source + +Installs Erlang from source. + +## erlang_solutions + +Adds Erlang Solutions' [package repositories][] on Debian, CentOS (> +5), and Fedora systems, and installs the `esl-erlang` package. + +[package repositories]:https://www.erlang-solutions.com/downloads/download-erlang-otp + +License and Author +================== + +* Author: Joe Williams () +* Author: Joshua Timberman () +* Author: Matt Ray () +* Author: Hector Castro () +* Author: Christopher Maier () + +Copyright 2011-2013, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/erlang/TESTING.md b/chef/cookbooks/erlang/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/erlang/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/erlang/attributes/default.rb b/chef/cookbooks/erlang/attributes/default.rb new file mode 100644 index 0000000..26ff0af --- /dev/null +++ b/chef/cookbooks/erlang/attributes/default.rb @@ -0,0 +1,26 @@ +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['erlang']['gui_tools'] = false +default['erlang']['install_method'] = "package" + +default['erlang']['source']['version'] = "R15B01" +default['erlang']['source']['url'] = "http://erlang.org/download/otp_src_#{node['erlang']['source']['version']}.tar.gz" +default['erlang']['source']['checksum'] = "f94f7de7328af3c0cdc42089c1a4ecd03bf98ec680f47eb5e6cddc50261cabde" + +default['erlang']['esl']['version'] = nil +default['erlang']['esl']['lsb_codename'] = node['lsb']['codename'] diff --git a/chef/cookbooks/erlang/chefignore b/chef/cookbooks/erlang/chefignore new file mode 100644 index 0000000..a6de142 --- /dev/null +++ b/chef/cookbooks/erlang/chefignore @@ -0,0 +1,96 @@ +# Put files/directories that should be ignored in this file when uploading +# or sharing to the community site. +# Lines that start with '# ' are comments. + +# OS generated files # +###################### +.DS_Store +Icon? +nohup.out +ehthumbs.db +Thumbs.db + +# SASS # +######## +.sass-cache + +# EDITORS # +########### +\#* +.#* +*~ +*.sw[a-z] +*.bak +REVISION +TAGS* +tmtags +*_flymake.* +*_flymake +*.tmproj +.project +.settings +mkmf.log + +## COMPILED ## +############## +a.out +*.o +*.pyc +*.so +*.com +*.class +*.dll +*.exe +*/rdoc/ + +# Testing # +########### +.watchr +.rspec +spec/* +spec/fixtures/* +test/* +features/* +Guardfile +Procfile + +# SCM # +####### +.git +*/.git +.gitignore +.gitmodules +.gitconfig +.gitattributes +.svn +*/.bzr/* +*/.hg/* +*/.svn/* + +# Berkshelf # +############# +Berksfile +Berksfile.lock +cookbooks/* +tmp + +# Cookbooks # +############# +CONTRIBUTING +CHANGELOG* + +# Strainer # +############ +Colanderfile +Strainerfile +.colander +.strainer + +# Vagrant # +########### +.vagrant +Vagrantfile + +# Travis # +########## +.travis.yml diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..816d43a --- /dev/null +++ b/chef/cookbooks/erlang/files/default/tests/minitest/default_test.rb @@ -0,0 +1,39 @@ +# +# Cookbook:: erlang +# Minitest Chef Handler +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'erlang::default' do + include Helpers::Erlang + + it 'doesnt install the gui_tools if the attribute is false (default)' do + skip unless node['platform_family'] == 'debian' + skip if node['erlang']['gui_tools'] + package("erlang-gs").wont_be_installed + end + + it 'can process erlang code with the erl command ' do + erl = shell_out("erl -myflag 1 <<-EOH +init:get_argument(myflag). +EOH +") + assert_includes(erl.stdout,'{ok,[["1"]]}') + end +end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb new file mode 100644 index 0000000..c9ba034 --- /dev/null +++ b/chef/cookbooks/erlang/files/default/tests/minitest/esl_test.rb @@ -0,0 +1,35 @@ +# +# Cookbook:: erlang_test +# Minitest Chef Handler +# +# Copyright:: Copyright (c) 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require File.expand_path('../support/helpers', __FILE__) + +describe_recipe 'erlang::erlang_solutions' do + include Helpers::Erlang + + it 'installs the esl-erlang package' do + package("esl-erlang").must_be_installed + end + + it "can successfully run 'erl'" do + erl = shell_out("erl -myflag 1 <<-EOH +init:get_argument(myflag). +EOH +") + assert_includes(erl.stdout,'{ok,[["1"]]}') + end +end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb b/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb new file mode 100644 index 0000000..3d7455f --- /dev/null +++ b/chef/cookbooks/erlang/files/default/tests/minitest/gui_tools_test.rb @@ -0,0 +1,30 @@ +# +# Cookbook:: erlang +# Minitest Chef Handler +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'erlang::default' do + include Helpers::Erlang + + it 'installs the x11 package if gui_tools is true' do + skip unless node['platform_family'] == 'debian' + package("erlang-gs").must_be_installed + end +end diff --git a/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..f0b32e4 --- /dev/null +++ b/chef/cookbooks/erlang/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,29 @@ +# +# Cookbook:: erlang +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Helpers + module Erlang + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + end +end diff --git a/chef/cookbooks/erlang/metadata.rb b/chef/cookbooks/erlang/metadata.rb new file mode 100644 index 0000000..cce048e --- /dev/null +++ b/chef/cookbooks/erlang/metadata.rb @@ -0,0 +1,19 @@ +name "erlang" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs erlang, optionally install GUI tools." +version "1.3.3" + +depends "apt", ">= 1.7.0" +depends "yum", ">= 0.5.0" +depends "build-essential" + +recipe "erlang", "Installs Erlang via native package, source, or Erlang Solutions package" +recipe "erlang::package", "Installs Erlang via native package" +recipe "erlang::source", "Installs Erlang via source" +recipe "erlang::esl", "Installs Erlang from Erlang Solutions' package repositories" + +%w{ ubuntu debian redhat centos fedora scientific amazon oracle }.each do |os| + supports os +end diff --git a/chef/cookbooks/erlang/recipes/default.rb b/chef/cookbooks/erlang/recipes/default.rb new file mode 100644 index 0000000..059a8db --- /dev/null +++ b/chef/cookbooks/erlang/recipes/default.rb @@ -0,0 +1,23 @@ +# Cookbook Name:: erlang +# Recipe:: default +# Author:: Joe Williams +# Author:: Matt Ray +# Author:: Hector Castro +# +# Copyright 2008-2009, Joe Williams +# Copyright 2011, Opscode Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "erlang::#{node["erlang"]["install_method"]}" diff --git a/chef/cookbooks/erlang/recipes/esl.rb b/chef/cookbooks/erlang/recipes/esl.rb new file mode 100644 index 0000000..c14c6b7 --- /dev/null +++ b/chef/cookbooks/erlang/recipes/esl.rb @@ -0,0 +1,92 @@ +# +# Cookbook Name:: erlang +# Recipe:: esl +# +# Author:: Christopher Maier () +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Install Erlang/OTP from Erlang Solutions + +case node['platform_family'] +when 'debian' + + include_recipe 'apt' + + apt_repository 'erlang_solutions_repo' do + uri 'http://binaries.erlang-solutions.com/debian' + distribution node['erlang']['esl']['lsb_codename'] + components ['contrib'] + key 'http://binaries.erlang-solutions.com/debian/erlang_solutions.asc' + action :add + end + +when 'rhel' + case node['platform'] + when 'centos', 'fedora' + + if platform?('centos') && node['platform_version'].to_i == 5 + Chef::Log.fatal("Erlang Solutions pacakge repositories are not available for Centos 5") + raise + else + + include_recipe 'yum' + + if platform?('centos') + include_recipe "yum::repoforge" + end + + yum_key "RPM-KEY-Erlang-Solutions" do + # Yes, yes, I know the URL has 'debian' in it... that's the address + url "http://binaries.erlang-solutions.com/debian/erlang_solutions.asc" + action :add + end + + # This replicates the files found at + # http://binaries.erlang-solutions.com/rpm/fedora/erlang_solutions.repo + # http://binaries.erlang-solutions.com/rpm/centos/erlang_solutions.repo + yum_repository "erlang-solutions" do + description "#{node['platform']} $releasever - $basearch - Erlang Solutions" + url "http://binaries.erlang-solutions.com/rpm/#{node['platform']}/$releasever/$basearch" + key "RPM-KEY-Erlang-Solutions" + enabled 1 + end + end + else + Chef::Log.fatal("Erlang Solutions pacakge repositories are currently not supported for RHEL family #{node['platform']} systems") + raise + end +else + Chef::Log.fatal("Erlang Solutions pacakge repositories are currently not supported for #{node['platform_family']} systems") + raise +end + +package "esl-erlang" do + version node['erlang']['esl']['version'] if node['erlang']['esl']['version'] +end + +# There's a small bug in the package for Ubuntu 10.04... this fixes +# it. Solution found at +# https://github.com/davidcoallier/bigcouch/blob/f6a6daf7590ecbab4d9dc4747624573b3137dfad/README.md#ubuntu-1004-lts-potential-issues +if platform?("ubuntu") && node['platform_version'] == "10.04" + bash "ubuntu-10.04-LTS-erlang-fix" do + user "root" + cwd "/usr/lib/erlang/man/man5" + code <<-EOS + rm modprobe.d.5 + ln -s modprobe.conf.5.gz modprobe.d.5 + EOS + end +end diff --git a/chef/cookbooks/erlang/recipes/package.rb b/chef/cookbooks/erlang/recipes/package.rb new file mode 100644 index 0000000..50ec677 --- /dev/null +++ b/chef/cookbooks/erlang/recipes/package.rb @@ -0,0 +1,49 @@ +# Cookbook Name:: erlang +# Recipe:: default +# Author:: Joe Williams +# Author:: Matt Ray +# Author:: Hector Castro +# +# Copyright 2008-2009, Joe Williams +# Copyright 2011, Opscode Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] +when "debian" + + erlpkg = node['erlang']['gui_tools'] ? "erlang-x11" : "erlang-nox" + + package erlpkg + package "erlang-dev" + +when "rhel" + + include_recipe "yum::epel" + + yum_repository "erlang" do + name "EPELErlangrepo" + url "http://repos.fedorapeople.org/repos/peter/erlang/epel-5Server/$basearch" + description "Updated erlang yum repository for RedHat / Centos 5.x - #{node['kernel']['machine']}" + action :add + only_if { node['platform_version'].to_f >= 5.0 && node['platform_version'].to_f < 6.0 } + end + + package "erlang" + +else + + package "erlang" + +end diff --git a/chef/cookbooks/erlang/recipes/source.rb b/chef/cookbooks/erlang/recipes/source.rb new file mode 100644 index 0000000..37b92a6 --- /dev/null +++ b/chef/cookbooks/erlang/recipes/source.rb @@ -0,0 +1,56 @@ +# Cookbook Name:: erlang +# Recipe:: default +# Author:: Joe Williams +# Author:: Matt Ray +# Author:: Hector Castro +# +# Copyright 2008-2009, Joe Williams +# Copyright 2011, Opscode Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "build-essential" + +erlang_deps = case node["platform_family"] + when "debian" + [ "libncurses5-dev", "openssl", "libssl-dev" ] + when "rhel", "fedora" + [ "ncurses-devel", "openssl-devel" ] + else + [ ] + end + +erlang_deps.each do |pkg| + package pkg do + action :install + end +end + +bash "install-erlang" do + cwd Chef::Config[:file_cache_path] + code <<-EOH + tar -xzf otp_src_#{node['erlang']['source']['version']}.tar.gz + (cd otp_src_#{node['erlang']['source']['version']} && ./configure && make && make install) + EOH + action :nothing + not_if "erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell | grep #{node['erlang']['source']['version']}" +end + +remote_file File.join(Chef::Config[:file_cache_path], "otp_src_#{node['erlang']['source']['version']}.tar.gz") do + source node['erlang']['source']['url'] + owner "root" + mode 0644 + checksum node['erlang']['source']['checksum'] + notifies :run, "bash[install-erlang]", :immediately +end diff --git a/chef/cookbooks/git/.gitignore b/chef/cookbooks/git/.gitignore new file mode 100644 index 0000000..dd1e425 --- /dev/null +++ b/chef/cookbooks/git/.gitignore @@ -0,0 +1,14 @@ +.vagrant +Berksfile.lock +Gemfile.lock +*~ +*# +.#* +\#*# +.*.sw[a-z] +*.un~ +.bundle +.cache +.kitchen +bin +.kitchen.local.yml diff --git a/chef/cookbooks/git/.kitchen.yml b/chef/cookbooks/git/.kitchen.yml new file mode 100644 index 0000000..2240f65 --- /dev/null +++ b/chef/cookbooks/git/.kitchen.yml @@ -0,0 +1,46 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: + - recipe[git] + attributes: {} + +- name: source + excludes: ["ubuntu-12.04", "ubuntu-10.04"] + run_list: + - recipe[git::source] + attributes: {} + +- name: server + run_list: + - recipe[git::server] + attributes: {} diff --git a/chef/cookbooks/git/Berksfile b/chef/cookbooks/git/Berksfile new file mode 100644 index 0000000..34a3b2d --- /dev/null +++ b/chef/cookbooks/git/Berksfile @@ -0,0 +1,8 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" + cookbook "yum" +end diff --git a/chef/cookbooks/git/CHANGELOG.md b/chef/cookbooks/git/CHANGELOG.md new file mode 100644 index 0000000..366065b --- /dev/null +++ b/chef/cookbooks/git/CHANGELOG.md @@ -0,0 +1,78 @@ +git Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the git cookbook. + + +v2.6.0 +------ +### Improvement +- **[COOK-3193](https://tickets.opscode.com/browse/COOK-3193)** - Add proper debian packages + +v2.5.2 +------ +### Bug +- [COOK-2813]: Fix bad string interpolation in source recipe + +v2.5.0 +------ +- Relax runit version constraint (now depend on 1.0+). + +v2.4.0 +------ +- [COOK-2734] - update git versions + +v2.3.0 +------ +- [COOK-2385] - update git::server for `runit_service` resource support + +v2.2.0 +------ +- [COOK-2303] - git::server support for RHEL `platform_family` + +v2.1.4 +------ +- [COOK-2110] - initial test-kitchen support (only available in GitHub repository) +- [COOK-2253] - pin runit dependency + +v2.1.2 +------ +- [COOK-2043] - install git on ubuntu 12.04 not git-core + +v2.1.0 +------ +The repository didn't have pushed commits, and so the following changes from earlier-than-latest versions wouldn't be available on the community site. We're releasing 2.1.0 to correct this. + +- [COOK-1943] - Update to git 1.8.0 +- [COOK-2020] - Add setup option attributes to Git Windows package install + +v2.0.0 +------- +This version uses `platform_family` attribute, making the cookbook incompatible with older versions of Chef/Ohai, hence the major version bump. + +- [COOK-1668] - git cookbook fails to run due to bad `platform_family` call +- [COOK-1759] - git::source needs additional package for rhel `platform_family` + +v1.1.2 +------ +- [COOK-2020] - Add setup option attributes to Git Windows package install + +v1.1.0 +------ +- [COOK-1943] - Update to git 1.8.0 + +v1.0.2 +------ +- [COOK-1537] - add recipe for source installation + +v1.0.0 +------ +- [COOK-1152] - Add support for Mac OS X +- [COOK-1112] - Add support for Windows + +v0.10.0 +------- +- [COOK-853] - Git client installation on CentOS + +v0.9.0 +------ +- Current public release diff --git a/chef/cookbooks/git/CONTRIBUTING b/chef/cookbooks/git/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/git/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/git/Gemfile b/chef/cookbooks/git/Gemfile new file mode 100644 index 0000000..46e0766 --- /dev/null +++ b/chef/cookbooks/git/Gemfile @@ -0,0 +1,3 @@ +source :rubygems + +gem 'test-kitchen', '< 1.0' diff --git a/chef/cookbooks/git/LICENSE b/chef/cookbooks/git/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/git/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/git/README.md b/chef/cookbooks/git/README.md new file mode 100644 index 0000000..1725052 --- /dev/null +++ b/chef/cookbooks/git/README.md @@ -0,0 +1,115 @@ +Description +=========== + +Installs git and optionally sets up a git server as a daemon under runit. + +Requirements +============ +## Ohai and Chef: + +* Ohai: 6.14.0+ + +This cookbook makes use of `node['platform_family']` to simplify platform +selection logic. This attribute was introduced in Ohai v0.6.12. + +## Platform: + +The following platform families are supported: + +* Debian +* Arch +* RHEL +* Fedora +* Mac OS X (10.6.0+) +* Windows + +## Cookbooks: + +* runit (for `git::server`) +* build-essential (for `git::source`) +* dmg (for OS X installation) +* yum (for RHEL 5 installation) + +### Windows Dependencies +The [`windows_package`](https://github.com/opscode-cookbooks/windows#windows_package) resource from the Windows cookbook is required to +install the git package on Windows. + +## Attributes + +### default +The following attributes are platform-specific. + +#### Windows + +* `node['git']['version']` - git version to install +* `node['git']['url']` - URL to git package +* `node['git']['checksum']` - package SHA256 checksum +* `node['git']['display_name']` - `windows_package` resource Display Name (makes the package install idempotent) + +#### Mac OS X + +* `node['git']['osx_dmg']['url']` - URL to git package +* `node['git']['osx_dmg']['checksum']` - package SHA256 checksum + +#### Linux + +* `node['git']['prefix']` - git install directory +* `node['git']['version']` - git version to install +* `node['git']['url']` - URL to git tarball +* `node['git']['checksum']` - tarball SHA256 checksum + +Recipes +======= + +## default + +Installs base git packages based on platform. + +## server + +Sets up a git daemon to provide a server. + +## source + +Installs git from source. + +## windows + +Installs git client on Windows + +Usage +===== + + +This cookbook primarily installs git core packages. It can also be +used to serve git repositories. + +To install git client (all supported platforms): + + include_recipe 'git' + +To install git server: + + include_recipe "git::server" + +This creates the directory specified by git/server/base_path (default is /srv/git) +and starts a git daemon, exporting all repositories found. Repositories need to be +added manually, but will be available once they are created. + +License and Author +================== + +- Author:: Joshua Timberman () +- Copyright:: 2009-2012, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/git/TESTING.md b/chef/cookbooks/git/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/git/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/git/attributes/default.rb b/chef/cookbooks/git/attributes/default.rb new file mode 100644 index 0000000..1830284 --- /dev/null +++ b/chef/cookbooks/git/attributes/default.rb @@ -0,0 +1,40 @@ +# +# Author:: Jamie Winsor () +# Cookbook Name:: git +# Attributes:: default +# +# Copyright 2008-2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +case node['platform_family'] +when 'windows' + default['git']['version'] = "1.8.1.2-preview20130201" + default['git']['url'] = "https://msysgit.googlecode.com/files/Git-#{node['git']['version']}.exe" + default['git']['checksum'] = "796ac91f0c7456b53f2717a81f475075cc581af2f447573131013cac5b63bb2a" + default['git']['display_name'] = "Git version #{ node['git']['version'] }" +when "mac_os_x" + default['git']['osx_dmg']['app_name'] = "git-1.8.2-intel-universal-snow-leopard" + default['git']['osx_dmg']['volumes_dir'] = "Git 1.8.2 Snow Leopard Intel Universal" + default['git']['osx_dmg']['package_id'] = "GitOSX.Installer.git182.git.pkg" + default['git']['osx_dmg']['url'] = "https://git-osx-installer.googlecode.com/files/git-1.8.2-intel-universal-snow-leopard.dmg" + default['git']['osx_dmg']['checksum'] = "e1d0ec7a9d9d03b9e61f93652b63505137f31217908635cdf2f350d07cb33e15" +else + default['git']['prefix'] = "/usr/local" + default['git']['version'] = "1.8.2.1" + default['git']['url'] = "https://nodeload.github.com/git/git/tar.gz/v#{node['git']['version']}" + default['git']['checksum'] = "bdc1768f70ce3d8f3e4edcdcd99b2f85a7f8733fb684398aebe58dde3e6bcca2" +end + +default['git']['server']['base_path'] = "/srv/git" +default['git']['server']['export_all'] = "true" diff --git a/chef/cookbooks/git/metadata.rb b/chef/cookbooks/git/metadata.rb new file mode 100644 index 0000000..83a9ce8 --- /dev/null +++ b/chef/cookbooks/git/metadata.rb @@ -0,0 +1,35 @@ +name "git" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs git and/or sets up a Git server daemon" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "2.6.1" +recipe "git", "Installs git" +recipe "git::server", "Sets up a runit_service for git daemon" +recipe "git::source", "Installs git from source" + +%w{ amazon arch centos debian fedora redhat scientific oracle amazon ubuntu windows }.each do |os| + supports os +end + +supports "mac_os_x", ">= 10.6.0" + +%w{ dmg build-essential yum windows }.each do |cookbook| + depends cookbook +end + +depends "runit", ">= 1.0" + +attribute "git/server/base_path", + :display_name => "Git Daemon Base Path", + :description => "A directory containing git repositories to be exposed by the git-daemon", + :default => "/srv/git", + :recipes => ["git::server"] + +attribute "git/server/export_all", + :display_name => "Git Daemon Export All", + :description => "Adds the --export-all option to the git-daemon parameters, making all repositories publicly readable even if they lack the \"git-daemon-export-ok\" file", + :choice => ["true", "false"], + :default => "true", + :recipes => ["git::server"] diff --git a/chef/cookbooks/git/recipes/default.rb b/chef/cookbooks/git/recipes/default.rb new file mode 100644 index 0000000..933face --- /dev/null +++ b/chef/cookbooks/git/recipes/default.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: git +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +case node['platform_family'] +when "debian" + if node['platform'] == "ubuntu" && node['platform_version'].to_f < 10.10 + package "git-core" + else + package "git" + end +when "rhel","fedora" + case node['platform_version'].to_i + when 5 + include_recipe "yum::epel" + end + package "git" +when "windows" + include_recipe 'git::windows' +when "mac_os_x" + dmg_package "GitOSX-Installer" do + app node['git']['osx_dmg']['app_name'] + package_id node['git']['osx_dmg']['package_id'] + volumes_dir node['git']['osx_dmg']['volumes_dir'] + source node['git']['osx_dmg']['url'] + checksum node['git']['osx_dmg']['checksum'] + type "pkg" + action :install + end +else + package "git" +end diff --git a/chef/cookbooks/git/recipes/server.rb b/chef/cookbooks/git/recipes/server.rb new file mode 100644 index 0000000..b67461c --- /dev/null +++ b/chef/cookbooks/git/recipes/server.rb @@ -0,0 +1,57 @@ +# +# Cookbook Name:: git +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if node["platform"] == "windows" + return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" +end + +include_recipe "git" + +directory node["git"]["server"]["base_path"] do + owner "root" + group "root" + mode 00755 +end + +case node['platform_family'] +when "debian" + include_recipe "runit" + + package "git-daemon-run" + + runit_service "git-daemon" do + sv_templates false + end +when "rhel" + package "git-daemon" + + template "/etc/xinetd.d/git" do + backup false + source "git-xinetd.d.erb" + owner "root" + group "root" + mode 00644 + end + + service "xinetd" do + action [:enable, :restart] + end +else + log "Platform requires setting up a git daemon service script." + log "Hint: /usr/bin/git daemon --export-all --user=nobody --group=daemon --base-path=#{node["git"]["server"]["base_path"]}" +end diff --git a/chef/cookbooks/git/recipes/source.rb b/chef/cookbooks/git/recipes/source.rb new file mode 100644 index 0000000..ec2b8c2 --- /dev/null +++ b/chef/cookbooks/git/recipes/source.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: git +# Recipe:: source +# +# Copyright 2012, Brian Flad, Fletcher Nichol +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if node["platform"] == "windows" + return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" +end + +include_recipe "build-essential" + +pkgs = value_for_platform_family( + ["rhel"] => %w{ expat-devel gettext-devel libcurl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel }, + ["debian"] => %w{ libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev } +) + +pkgs.each do |pkg| + package pkg +end + +remote_file "#{Chef::Config['file_cache_path']}/git-#{node['git']['version']}.tar.gz" do + source node['git']['url'] + checksum node['git']['checksum'] + mode 00644 + not_if "test -f #{Chef::Config['file_cache_path']}/git-#{node['git']['version']}.tar.gz" +end + +execute "Extracting and Building Git #{node['git']['version']} from Source" do + cwd Chef::Config['file_cache_path'] + command <<-COMMAND + (mkdir git-#{node['git']['version']} && tar -zxf git-#{node['git']['version']}.tar.gz -C git-#{node['git']['version']} --strip-components 1) + (cd git-#{node['git']['version']} && make prefix=#{node['git']['prefix']} install) + COMMAND + creates "#{node['git']['prefix']}/bin/git" + not_if "git --version | grep #{node['git']['version']}" +end diff --git a/chef/cookbooks/git/recipes/windows.rb b/chef/cookbooks/git/recipes/windows.rb new file mode 100644 index 0000000..957b94b --- /dev/null +++ b/chef/cookbooks/git/recipes/windows.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: git +# Recipe:: windows +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +windows_package node['git']['display_name'] do + action :install + source node['git']['url'] + checksum node['git']['checksum'] + installer_type :inno +end + +# Git is installed to Program Files (x86) on 64-bit machines and +# 'Program Files' on 32-bit machines +PROGRAM_FILES = ENV['ProgramFiles(x86)'] || ENV['ProgramFiles'] + +windows_path "#{ PROGRAM_FILES }\\Git\\Cmd" do + action :add +end diff --git a/chef/cookbooks/git/templates/default/git-xinetd.d.erb b/chef/cookbooks/git/templates/default/git-xinetd.d.erb new file mode 100644 index 0000000..e3cf2e6 --- /dev/null +++ b/chef/cookbooks/git/templates/default/git-xinetd.d.erb @@ -0,0 +1,10 @@ +service git +{ + disable = no + socket_type = stream + wait = no + user = nobody + server = /usr/libexec/git-core/git-daemon + server_args = --base-path=<%= node["git"]["server"]["base_path"] %> <% if node["git"]["server"]["export_all"] == "true" %>--export-all <% end %>--syslog --inetd --verbose + log_on_failure += USERID +} diff --git a/chef/cookbooks/git/templates/default/sv-git-daemon-log-run.erb b/chef/cookbooks/git/templates/default/sv-git-daemon-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/git/templates/default/sv-git-daemon-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/git/templates/default/sv-git-daemon-run.erb b/chef/cookbooks/git/templates/default/sv-git-daemon-run.erb new file mode 100644 index 0000000..123d7d8 --- /dev/null +++ b/chef/cookbooks/git/templates/default/sv-git-daemon-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec /usr/bin/git daemon <% if node["git"]["server"]["export_all"] == "true" %>--export-all <% end %>--user=nobody --group=daemon --syslog --base-path=<%= node["git"]["server"]["base_path"] %> <%= node["git"]["server"]["base_path"] %> diff --git a/chef/cookbooks/memcached/.kitchen.yml b/chef/cookbooks/memcached/.kitchen.yml new file mode 100644 index 0000000..b6c2c55 --- /dev/null +++ b/chef/cookbooks/memcached/.kitchen.yml @@ -0,0 +1,40 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: + - recipe[memcached] + attributes: {} + +- name: instance + run_list: + - recipe[memcached] + - recipe[memcached_test::instance] diff --git a/chef/cookbooks/memcached/Berksfile b/chef/cookbooks/memcached/Berksfile new file mode 100644 index 0000000..502b147 --- /dev/null +++ b/chef/cookbooks/memcached/Berksfile @@ -0,0 +1,9 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" + cookbook "yum" + cookbook "memcached_test", :path => "./test/cookbooks/memcached_test" +end diff --git a/chef/cookbooks/memcached/CHANGELOG.md b/chef/cookbooks/memcached/CHANGELOG.md new file mode 100644 index 0000000..e71b6ca --- /dev/null +++ b/chef/cookbooks/memcached/CHANGELOG.md @@ -0,0 +1,61 @@ +memcached Cookbook CHANGELOG +============================ +This file is used to list changes made in each version of the memcached cookbook. + + +v1.5.0 +------ +### Improvement +- **[COOK-3336](https://tickets.opscode.com/browse/COOK-3336)** - Add option to specify logfile +- **[COOK-3299](https://tickets.opscode.com/browse/COOK-3299)** - Document that `memcached` is exposed by default + +### Bug +- **[COOK-2990](https://tickets.opscode.com/browse/COOK-2990)** - Include `listen`, `maxconn`, and `user` in the runit service + +### New Feature +- **[COOK-2790](https://tickets.opscode.com/browse/COOK-2790)** - Add support for defining max object size + +v1.4.0 +------ +### Improvement +- [COOK-2756]: add SUSE support to memcached cookbook +- [COOK-2791]: Remove the template for Karmic from the memcached cookbook + +### Bug +- [COOK-2600]: support memcached on SmartOS + +v1.3.0 +------ +- [COOK-2386] - update `memcached_instance` definition for `runit_service` resource + +v1.2.0 +------ +- [COOK-1469] - include yum epel recipe on RHEL 5 (introduces yum cookbook dependency) +- [COOK-2202] - Fix typo in previous ticket/commits +- [COOK-2266] - pin runit dependency + +v1.1.2 +------ +- [COOK-990] - params insite runit_service isn't the same as outside + +v1.1.0 +------ +- [COOK-1764] - Add Max Connections to memcached.conf and fix typos + +v1.0.4 +------ +- [COOK-1192] - metadata doesn't include RH platforms (supported) +- [COOK-1354] - dev package changed name on centos6 + +v1.0.2 +------ +- [COOK-1081] - support for centos/rhel + +v1.0.0 +------ +- [COOK-706] - Additional info in README +- [COOK-828] - Package for RHEL systems + +v0.10.4 +------- +- Current released version diff --git a/chef/cookbooks/memcached/CONTRIBUTING b/chef/cookbooks/memcached/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/memcached/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/memcached/Gemfile b/chef/cookbooks/memcached/Gemfile new file mode 100644 index 0000000..46e0766 --- /dev/null +++ b/chef/cookbooks/memcached/Gemfile @@ -0,0 +1,3 @@ +source :rubygems + +gem 'test-kitchen', '< 1.0' diff --git a/chef/cookbooks/memcached/LICENSE b/chef/cookbooks/memcached/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/memcached/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/memcached/README.md b/chef/cookbooks/memcached/README.md new file mode 100644 index 0000000..8c41980 --- /dev/null +++ b/chef/cookbooks/memcached/README.md @@ -0,0 +1,82 @@ +Description +=========== + +Installs memcached and provides a define to set up an instance of +memcache via runit. + +Requirements +============ + +A runit service can be set up for instances using the +`memcache_instance` definition. + +## Platforms: + +Tested on: + +* Ubuntu 10.04, 12.04 +* CentOS 5.8, 6.3 +* openSUSE 12.3 +* SLES 12 SP2 +* SmartOS base64 1.8.1 + * Note that SMF directly configures memcached with no opportunity to alter settings. + If you need custom parameters, use the `memcached_instance` provider instead. + +May work on any Debian or Red Hat family distributions with or without +modification. + +## Cookbooks: + +* runit + +Attributes +========== + +The following are node attributes passed to the template for the runit +service. + +* `memcached['memory']` - maximum memory for memcached instances. +* `memcached['user']` - user to run memcached as. +* `memcached['port']` - port for memcached to listen on. +* `memcached['listen']` - IP address for memcache to listen on, defaults to **0.0.0.0** (world accessible). +* `memcached['maxconn']` - maximum number of connections to accept (defaults to 1024) +* `memcached['max_object_size']` - maximum size of an object to cache (defaults to 1MB) +* `memcached['logfilename']` - logfile to which memcached output will be redirected in /var/log/$logfilename. + +Usage +===== + +Simply set the attributes and it will configure the +/etc/memcached.conf file. If you want to use multiple memcached +instances, you'll need to modify the recipe to disable the startup +script and the template in the default recipe. + +Use the define, memcached_instance, to set up a runit service for the +named memcached instance. + + memcached_instance "myproj" + +The recipe also reads in whether to start up memcached from a +/etc/default/memcached "ENABLE_MEMCACHED" setting, which is "yes" by +default. + +License and Author +================== + +Author:: Joshua Timberman () +Author:: Joshua Sierles () + +Copyright:: 2009-2012, Opscode, Inc +Copyright:: 2009, 37signals + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/memcached/TESTING.md b/chef/cookbooks/memcached/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/memcached/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/memcached/attributes/default.rb b/chef/cookbooks/memcached/attributes/default.rb new file mode 100644 index 0000000..fe63541 --- /dev/null +++ b/chef/cookbooks/memcached/attributes/default.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: memcached +# Attributes:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['memcached']['memory'] = 64 +default['memcached']['port'] = 11211 +default['memcached']['listen'] = "0.0.0.0" +default['memcached']['maxconn'] = 1024 +default['memcached']['max_object_size'] = "1m" +default['memcached']['logfilename'] = "memcached.log" +case node['platform_family'] +when 'suse', 'fedora', 'rhel' + default['memcached']['user'] = 'memcached' + default['memcached']['group'] = 'memcached' +when 'debian', 'ubuntu' + default['memcached']['user'] = 'memcache' + default['memcached']['group'] = 'memcache' +else + default['memcached']['user'] = 'nobody' + default['memcached']['user'] = 'nogroup' +end diff --git a/chef/cookbooks/memcached/definitions/memcached_instance.rb b/chef/cookbooks/memcached/definitions/memcached_instance.rb new file mode 100644 index 0000000..fe44498 --- /dev/null +++ b/chef/cookbooks/memcached/definitions/memcached_instance.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: memcached +# Definition:: memcached_instance +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :memcached_instance do + include_recipe "runit" + include_recipe "memcached" + + opts = params + + runit_service "memcached-#{params[:name]}" do + run_template_name "memcached" + default_logger true + cookbook "memcached" + options({ + :memory => node['memcached']['memory'], + :port => node['memcached']['port'], + :listen => node['memcached']['listen'], + :maxconn => node['memcached']['maxconn'], + :user => node['memcached']['user']}.merge(opts) + ) + end +end diff --git a/chef/cookbooks/memcached/metadata.rb b/chef/cookbooks/memcached/metadata.rb new file mode 100644 index 0000000..d453023 --- /dev/null +++ b/chef/cookbooks/memcached/metadata.rb @@ -0,0 +1,40 @@ +name "memcached" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs memcached and provides a define to set up an instance of memcache via runit" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.5.0" +depends "runit", "~> 1.0" +depends "yum" + +recipe "memcached", "Installs and configures memcached" + +%w{ ubuntu debian redhat fedora centos + scientific amazon smartos suse }.each do |os| + supports os +end + +attribute "memcached/memory", + :display_name => "Memcached Memory", + :description => "Memory allocated for memcached instance", + :default => "64" + +attribute "memcached/port", + :display_name => "Memcached Port", + :description => "Port to use for memcached instance", + :default => "11211" + +attribute "memcached/user", + :display_name => "Memcached User", + :description => "User to run memcached instance as", + :default => "nobody" + +attribute "memcached/listen", + :display_name => "Memcached IP Address", + :description => "IP address to use for memcached instance", + :default => "0.0.0.0" +attribute "memcached/logfilename", + :display_name => "Memcached logfilename", + :description => "The filename used to log memcached", + :default => "memcached.log" diff --git a/chef/cookbooks/memcached/recipes/default.rb b/chef/cookbooks/memcached/recipes/default.rb new file mode 100644 index 0000000..ce016fb --- /dev/null +++ b/chef/cookbooks/memcached/recipes/default.rb @@ -0,0 +1,93 @@ +# +# Cookbook Name:: memcached +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# include epel on redhat/centos 5 and below in order to get the memcached packages +if node['platform_family'] == "rhel" and node['platform_version'].to_i < 6 + include_recipe "yum::epel" +end + +package "memcached" do + action :install +end + +package "libmemcache-dev" do + case node['platform_family'] + when "rhel", "fedora" + package_name "libmemcached-devel" + when "smartos" + package_name "libmemcached" + when "suse" + if node['platform_version'].to_f < 12 + package_name "libmemcache-devel" + else + package_name "libmemcached-devel" + end + else + package_name "libmemcache-dev" + end + action :install +end + +service "memcached" do + action :nothing + supports :status => true, :start => true, :stop => true, :restart => true +end + +case node['platform_family'] +when "rhel", "fedora", "suse" + family = node['platform_family'] == 'suse' ? 'suse' : 'redhat' + template "/etc/sysconfig/memcached" do + source "memcached.sysconfig.#{family}.erb" + owner "root" + group "root" + mode 00644 + variables( + :listen => node['memcached']['listen'], + :user => node['memcached']['user'], + :group => node['memcached']['group'], + :port => node['memcached']['port'], + :maxconn => node['memcached']['maxconn'], + :memory => node['memcached']['memory'], + :logfilename => node['memcached-chat']['logfilename'] + ) + notifies :restart, "service[memcached]" + end +when "smartos" + # SMF directly configures memcached with no opportunity to alter settings + # If you need custom parameters, use the memcached_instance provider + service "memcached" do + action :enable + end +else + template "/etc/memcached.conf" do + source "memcached.conf.erb" + owner "root" + group "root" + mode 00644 + variables( + :listen => node['memcached']['listen'], + :user => node['memcached']['user'], + :port => node['memcached']['port'], + :maxconn => node['memcached']['maxconn'], + :memory => node['memcached']['memory'], + :max_object_size => node['memcached']['max_object_size'] + ) + notifies :restart, "service[memcached]" + end +end diff --git a/chef/cookbooks/memcached/templates/default/memcached.conf.erb b/chef/cookbooks/memcached/templates/default/memcached.conf.erb new file mode 100644 index 0000000..85037d3 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/memcached.conf.erb @@ -0,0 +1,53 @@ +# +# Configured by Chef. Local changes will be lost. +# +# memcached default config file +# 2003 - Jay Bonci +# This configuration file is read by the start-memcached script provided as +# part of the Debian GNU/Linux distribution. + +# Run memcached as a daemon. This command is implied, and is not needed for the +# daemon to run. See the README.Debian that comes with this package for more +# information. +-d + +# Log memcached's output to /var/log/memcached +logfile /var/log/memcached.log + +# Be verbose +-v + +# Be even more verbose (print client commands as well) +# -vv + +# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default +# Note that the daemon will grow to this size, but does not start out holding this much +# memory +-m <%= @memory %> + +# Default connection port is 11211 +-p <%= @port %> + +# Run the daemon as root. The start-memcached will default to running as root if no +# -u command is present in this config file +-u <%= @user %> + +# Specify which IP address to listen on. The default is to listen on all IP addresses +# This parameter is one of the only security measures that memcached has, so make sure +# it's listening on a firewalled interface. +-l <%= @listen %> + +# Limit the number of simultaneous incoming connections. The daemon default is 1024 +-c <%= @maxconn %> + +# Lock down all paged memory. Consult with the README and homepage before you do this +# -k + +# Return error when memory is exhausted (rather than removing items) +# -M + +# Maximize core file limit +# -r + +# Max object size +-I <%= @max_object_size %> diff --git a/chef/cookbooks/memcached/templates/default/memcached.default.erb b/chef/cookbooks/memcached/templates/default/memcached.default.erb new file mode 100644 index 0000000..96258c5 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/memcached.default.erb @@ -0,0 +1,2 @@ +# Set this to yes to enable memcached. +ENABLE_MEMCACHED=yes diff --git a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb new file mode 100644 index 0000000..5ec7c18 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.redhat.erb @@ -0,0 +1,13 @@ +# +# Configured by Chef. Local changes will be lost. +# +# "Javier Frias" +# +# centos sysconfig memcached default file +# + +PORT="<%= @port %>" +USER="<%= @user %>" +MAXCONN="<%= @maxconn %>" +CACHESIZE="<%= @memory %>" +OPTIONS="-l <%= @listen %> >> /var/log/<%= @logfilename %> 2>&1" diff --git a/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb new file mode 100644 index 0000000..9a1b360 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/memcached.sysconfig.suse.erb @@ -0,0 +1,32 @@ +## Path: Network/WWW/Memcached +## Description: start parameters for memcached. +## Type: string +## Default: "-d -l 127.0.0.1" +## Config: memcached +# +# start parameters for memcached. +# +# see man 1 memcached for more +# +MEMCACHED_PARAMS="<%= "-l #{@listen} -c #{@maxconn} " + + "-m #{@memory} -p #{@port} >> /var/log/#{@logfilename} 2>&1"%>" + +## Path: Network/WWW/Memcached +## Description: username memcached should run as +## Type: string +## Default: "memcached" +## Config: memcached +# +# username memcached should run as +# +MEMCACHED_USER="<%= @user %>" + +## Path: Network/WWW/Memcached +## Description: group memcached should be run as +## Type: string +## Default: "memcached" +## Config: memcached +# +# group memcached should be run as +# +MEMCACHED_GROUP="<%= @group %>" diff --git a/chef/cookbooks/memcached/templates/default/sv-memcached-log-run.erb b/chef/cookbooks/memcached/templates/default/sv-memcached-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/sv-memcached-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb b/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb new file mode 100644 index 0000000..28c2fd5 --- /dev/null +++ b/chef/cookbooks/memcached/templates/default/sv-memcached-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec chpst -u <%= @options[:user] %> /usr/bin/memcached -v -m <%= @options[:memory] %> -p <%= @options[:port] %> -u <%= @options[:user] %> -l <%= @options[:listen] %> -c <%= @options[:maxconn] %> diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md b/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md new file mode 100644 index 0000000..da45e2e --- /dev/null +++ b/chef/cookbooks/memcached/test/cookbooks/memcached_test/README.md @@ -0,0 +1 @@ +This cookbook is used with test-kitchen to test the parent, memcached cookbok diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb b/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb new file mode 100644 index 0000000..e75ea4f --- /dev/null +++ b/chef/cookbooks/memcached/test/cookbooks/memcached_test/metadata.rb @@ -0,0 +1,6 @@ +name "memcached_test" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "This cookbook is used with test-kitchen to test the parent, memcached cookbok" +version "1.0.0" diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb b/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb new file mode 100644 index 0000000..22024f7 --- /dev/null +++ b/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: memcached_test +# Recipe:: default +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "memcached::default" diff --git a/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb b/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb new file mode 100644 index 0000000..f7fe2ae --- /dev/null +++ b/chef/cookbooks/memcached/test/cookbooks/memcached_test/recipes/instance.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: memcached_test +# Recipe:: instance +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "memcached::default" + +m = resources("service[memcached]") +m.action :stop + +memcached_instance "myproj" do + port 11212 + memory 128 +end diff --git a/chef/cookbooks/mysql/.kitchen.yml b/chef/cookbooks/mysql/.kitchen.yml new file mode 100644 index 0000000..3bc37ab --- /dev/null +++ b/chef/cookbooks/mysql/.kitchen.yml @@ -0,0 +1,48 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + run_list: + - recipe[yum::epel] + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + run_list: + - recipe[yum::epel] + +suites: +- name: client + run_list: + - recipe[mysql::client] + attributes: {} +- name: ruby + run_list: + - recipe[mysql::ruby] + attributes: {} +- name: server + run_list: + - recipe[minitest-handler] + - recipe[mysql_test::server] + attributes: {} diff --git a/chef/cookbooks/mysql/Berksfile b/chef/cookbooks/mysql/Berksfile new file mode 100644 index 0000000..bdbe545 --- /dev/null +++ b/chef/cookbooks/mysql/Berksfile @@ -0,0 +1,11 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" + cookbook "yum" + + cookbook "mysql_test", :path => "./test/cookbooks/mysql_test" + cookbook "minitest-handler" +end diff --git a/chef/cookbooks/mysql/CHANGELOG.md b/chef/cookbooks/mysql/CHANGELOG.md new file mode 100644 index 0000000..73aff60 --- /dev/null +++ b/chef/cookbooks/mysql/CHANGELOG.md @@ -0,0 +1,155 @@ +mysql Cookbook CHANGELOG +======================== +This file is used to list changes made in each version of the mysql cookbook. + +v3.0.4 +------ +### Bug +- **[COOK-3310](https://tickets.opscode.com/browse/COOK-3310)** - Fix missing `GRANT` option +- **[COOK-3233](https://tickets.opscode.com/browse/COOK-3233)** - Fix escaping special characters +- **[COOK-3156](https://tickets.opscode.com/browse/COOK-3156)** - Fix GRANTS file when `remote_root_acl` is specified +- **[COOK-3134](https://tickets.opscode.com/browse/COOK-3134)** - Fix Chef 11 support +- **[COOK-2318](https://tickets.opscode.com/browse/COOK-2318)** - Remove redundant `if` block around `node.mysql.tunable.log_bin` + +v3.0.2 +------ +### Bug +- [COOK-2158]: apt-get update is run twice at compile time +- [COOK-2832]: mysql grants.sql file has errors depending on attrs +- [COOK-2995]: server.rb is missing a platform_family comparison value + +### Sub-task +- [COOK-2102]: `innodb_flush_log_at_trx_commit` value is incorrectly set based on CPU count + +v3.0.0 +------ +**Note** This is a backwards incompatible version with previous versions of the cookbook. Tickets that introduce incompatibility are COOK-2615 and COOK-2617. + +- [COOK-2478] - Duplicate 'read_only' server attribute in base and tunable +- [COOK-2471] - Add tunable to set slave_compressed_protocol for reduced network traffic +- [COOK-1059] - Update attributes in mysql cookbook to support missing options for my.cnf usable by Percona +- [COOK-2590] - Typo in server recipe to do with conf_dir and confd_dir +- [COOK-2602] - Add `lower_case_table_names` tunable +- [COOK-2430] - Add a tunable to create a network ACL when allowing `remote_root_access` +- [COOK-2619] - mysql: isamchk deprecated +- [COOK-2515] - Better support for SUSE distribution for mysql cookbook +- [COOK-2557] - mysql::percona_repo attributes missing and key server typo +- [COOK-2614] - Duplicate `innodb_file_per_table` +- [COOK-2145] - MySQL cookbook should remove anonymous and password less accounts +- [COOK-2553] - Enable include directory in my.cnf template for any platform +- [COOK-2615] - Rename `key_buffer` to `key_buffer_size` +- [COOK-2626] - Percona repo URL is being constructed incorrectly +- [COOK-2616] - Unneeded attribute thread_cache +- [COOK-2618] - myisam-recover not using attribute value +- [COOK-2617] - open-files is a duplicate of open-files-limit + +v2.1.2 +------ +- [COOK-2172] - Mysql cookbook duplicates `binlog_format` configuration + +v2.1.0 +------ +- [COOK-1669] - Using platform("ubuntu") in default attributes always returns true +- [COOK-1694] - Added additional my.cnf fields and reorganized cookbook to avoid race conditions with mysql startup and sql script execution +- [COOK-1851] - Support server-id and binlog_format settings +- [COOK-1929] - Update msyql server attributes file because setting attributes without specifying a precedence is deprecated +- [COOK-1999] - Add read_only tunable useful for replication slave servers + +v2.0.2 +------ +- [COOK-1967] - mysql: trailing comma in server.rb platform family + +v2.0.0 +------ +**Important note for this release** + +Under Chef Solo, you must set the node attributes for the root, debian and repl passwords or the run will completely fail. See COOK-1737 for background on this. + +- [COOK-1390] - MySQL service cannot start after reboot +- [COOK-1610] - Set root password outside preseed (blocker for drop-in mysql replacements) +- [COOK-1624] - Mysql cookbook fails to even compile on windows +- [COOK-1669] - Using platform("ubuntu") in default attributes always returns true +- [COOK-1686] - Add mysql service start +- [COOK-1687] - duplicate `innodb_buffer_pool_size` attribute +- [COOK-1704] - mysql cookbook fails spec tests when minitest-handler cookbook enabled +- [COOK-1737] - Fail a chef-solo run when `server_root_password`, `server_debian_password`, and/or `server_repl_password` is not set +- [COOK-1769] - link to database recipe in mysql README goes to old opscode/cookbooks repo instead of opscode-cookbook organization +- [COOK-1963] - use `platform_family` + +v1.3.0 +------ +**Important note for this release** + +This version no longer installs Ruby bindings in the client recipe by default. Use the ruby recipe if you'd like the RubyGem. If you'd like packages from your distribution, use them in your application's specific cookbook/recipe, or modify the client packages attribute. This resolves the following tickets: + +- COOK-932 +- COOK-1009 +- COOK-1384 + +Additionally, this cookbook now has tests (COOK-1439) for use under test-kitchen. + +The following issues are also addressed in this release. + +- [COOK-1443] - MySQL (>= 5.1.24) does not support `innodb_flush_method` = fdatasync +- [COOK-1175] - Add Mac OS X support +- [COOK-1289] - handle additional tunable attributes +- [COOK-1305] - add auto-increment-increment and auto-increment-offset attributes +- [COOK-1397] - make the port an attribute +- [COOK-1439] - Add MySQL cookbook tests for test-kitchen support +- [COOK-1236] - Move package names into attributes to allow percona to free-ride +- [COOK-934] - remove deprecated mysql/libraries/database.rb, use the database cookbook instead. +- [COOK-1475] - fix restart on config change + +v1.2.6 +------ +- [COOK-1113] - Use an attribute to determine if upstart is used +- [COOK-1121] - Add support for Windows +- [COOK-1140] - Fix conf.d on Debian +- [COOK-1151] - Fix server_ec2 handling /var/lib/mysql bind mount +- [COOK-1321] - Document setting password attributes for solo + +v1.2.4 +------ +- [COOK-992] - fix FATAL nameerror +- [COOK-827] - `mysql:server_ec2` recipe can't mount `data_dir` +- [COOK-945] - FreeBSD support + +v1.2.2 +------ +- [COOK-826] mysql::server recipe doesn't quote password string +- [COOK-834] Add 'scientific' and 'amazon' platforms to mysql cookbook + +v1.2.1 +------ +- [COOK-644] Mysql client cookbook 'package missing' error message is confusing +- [COOK-645] RHEL6/CentOS6 - mysql cookbook contains 'skip-federated' directive which is unsupported on MySQL 5.1 + +v1.2.0 +------ +- [COOK-684] remove mysql_database LWRP + +v1.0.8 +------ +- [COOK-633] ensure "cloud" attribute is available + +v1.0.7 +------ +- [COOK-614] expose all mysql tunable settings in config +- [COOK-617] bind to private IP if available + +v1.0.6 +------ +- [COOK-605] install mysql-client package on ubuntu/debian + +v1.0.5 +------ +- [COOK-465] allow optional remote root connections to mysql +- [COOK-455] improve platform version handling +- externalize conf_dir attribute for easier cross platform support +- change datadir attribute to data_dir for consistency + +v1.0.4 +------ +- fix regressions on debian platform +- [COOK-578] wrap root password in quotes +- [COOK-562] expose all tunables in my.cnf diff --git a/chef/cookbooks/mysql/CONTRIBUTING b/chef/cookbooks/mysql/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/mysql/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/mysql/LICENSE b/chef/cookbooks/mysql/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/mysql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/mysql/README.md b/chef/cookbooks/mysql/README.md new file mode 100644 index 0000000..9c935d5 --- /dev/null +++ b/chef/cookbooks/mysql/README.md @@ -0,0 +1,220 @@ +mysql Cookbook +============== +Installs and configures MySQL client or server. + + +Requirements +------------ +Chef 0.10.10+. + + +Platform +-------- +- Debian, Ubuntu +- CentOS, Red Hat, Fedora +- Mac OS X (Using homebrew) + +Tested on: + +- Debian 5.0, 6.0 +- Ubuntu 10.04-12.04 +- CentOS 5.5-5.8, 6.2-6.3 +- Mac OS X 10.7.2 + +See TESTING.md for information about running tests in Opscode's Test Kitchen. + + +Cookbooks +--------- +Requires Opscode's openssl cookbook for secure password generation. See _Attributes_ and _Usage_ for more information. + +The RubyGem installation in the `mysql::ruby` recipe requires a C compiler and Ruby development headers to be installed in order to build the mysql gem. + +Requires `homebrew` [cookbook](http://community.opscode.com/cookbooks/homebrew) on Mac OS X. + + +Resources and Providers +----------------------- +The LWRP that used to ship as part of this cookbook has been refactored into the +[database](http://community.opscode.com/cookbooks/database) cookbook. Please see the README for details on updated usage. + + +Attributes +---------- +See the `attributes/server.rb` or `attributes/client.rb` for default values. Several attributes have values that vary based on the node's platform and version. + +* `node['mysql']['client']['packages']` - An array of package names + that should be installed on "client" systems. This can be modified, + e.g., to specify packages for Percona. +* `node['mysql']['server']['packages']` - An array of package names + that should be installed on "server" systems. This can be modified, + e.g., to specify packages for Percona. + +* `node['mysql']['auto-increment-increment']` - + auto-increment-increment value in my.cnf +* `node['mysql']['auto-increment-offset]` - auto-increment-offset + value in my.cnf +* `node['mysql']['basedir']` - Base directory where MySQL is installed +* `node['mysql']['bind_address']` - Listen address for MySQLd +* `node['mysql']['conf_dir']` - Location for mysql conf directory +* `node['mysql']['confd_dir']` - Location for mysql conf.d style + include directory +* `node['mysql']['data_dir']` - Location for mysql data directory +* `node['mysql']['ec2_path']` - location of mysql data_dir on EC2 + nodes +* `node['mysql']['grants_path']` - Path where the grants.sql should be + written +* `node['mysql']['mysqladmin_bin']` - Path to the mysqladmin binary +* `node['mysql']['old_passwords']` - Sets the `old_passwords` value in + my.cnf. +* `node['mysql']['pid_file']` - Path to the mysqld.pid file +* `node['mysql']['port']` - Liten port for MySQLd +* `node['mysql']['reload_action']` - Action to take when mysql conf + files are modified. Also allows "reload" and "none". +* `node['mysql']['root_group']` - The default group of the "root" user +* `node['mysql']['service_name']` - The name of the mysqld service +* `node['mysql']['socket']` - Path to the mysqld.sock file +* `node['mysql']['use_upstart']` - Whether to use upstart for the + service provider +* `mysql['root_network_acl']` - Set define the network the root user will be able to login from, default is nil + +Performance and other "tunable" attributes are under the `node['mysql']['tunable']` attribute, corresponding to the same-named parameter in my.cnf, and the default values are used. See `attributes/server.rb`. + +By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. + +* `node['mysql']['remove_anonymous_users']` - Remove anonymous users + +Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. + +* `node['mysql']['allow_remote_root']` - If true Sets root access from '%'. If false deletes any non-localhost root users. + +By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. This will also drop any user privileges to the test databae and any DB named test_% . + +* `node['mysql']['remove_test_database']` - Delete the test database and access to it. + +The following attributes are randomly generated passwords handled in the `mysql::server` recipe, using the OpenSSL cookbook's `secure_password` helper method. These are set using the `set_unless` node attribute method, which allows them to be easily overridden e.g. +in a role. + +* `node['mysql']['server_root_password']` - Set the server's root + password +* `node['mysql']['server_repl_password']` - Set the replication user + 'repl' password +* `node['mysql']['server_debian_password']` - Set the debian-sys-maint + user password + +### Windows Specific + +The following attributes are specific to Windows platforms. + +* `node['mysql']['client']['version']` - The version of MySQL + connector to install. +* `node['mysql']['client']['arch']` - Force 32 bit to work with the + mysql gem +* `node['mysql']['client']['package_file']` - The MSI file for the + mysql connector. +* `node['mysql']['client']['url']` - URL to download the mysql + connector. +* `node['mysql']['client']['packages']` - Similar to other platforms, + this is the name of the client package. +* `node['mysql']['client']['basedir']` - Base installation location +* `node['mysql']['client']['lib_dir']` - Libraries under the base location +* `node['mysql']['client']['bin_dir']` - binary directory under base location +* `node['mysql']['client']['ruby_dir']` - location where the Ruby + binaries will be + + +Usage +----- +On client nodes, use the client (or default) recipe: + +```javascript +{ "run_list": ["recipe[mysql::client]"] } +``` + +This will install the MySQL client libraries and development headers on the system. + +On nodes which may use the `database` cookbook's mysql resources, also use the ruby recipe. This installs the mysql RubyGem in the Ruby environment Chef is using via `chef_gem`. + +```javascript +{ "run_list": ["recipe[mysql::client]", "recipe[mysql::ruby]"] } +``` + +If you need to install the mysql Ruby library as a package for your system, override the client packages attribute in your node or role. For example, on an Ubuntu system: + +```javascript +{ + "mysql": { + "client": { + "packages": ["mysql-client", "libmysqlclient-dev","ruby-mysql"] + } + } +} +``` + +This creates a resource object for the package and does the installation before other recipes are parsed. You'll need to have the C compiler and such (ie, build-essential on Ubuntu) before running the recipes, but we already do that when installing Chef :-). + +On server nodes, use the server recipe: + +```javascript +{ "run_list": ["recipe[mysql::server]"] } +``` + +On Debian and Ubuntu, this will preseed the mysql-server package with the randomly generated root password in the recipe file. On other platforms, it simply installs the required packages. It will also create an SQL file, `/etc/mysql/grants.sql`, that will be used to set up grants for the root, repl and debian-sys-maint users. + +The recipe will perform a `node.save` unless it is run under `chef-solo` after the password attributes are used to ensure that in the event of a failed run, the saved attributes would be used. + +On EC2 nodes, use the `server_ec2` recipe and the mysql data dir will be set up in the ephmeral storage. + +```javascript +{ "run_list": ["recipe[mysql::server_ec2]"] } +``` + +When the `ec2_path` doesn't exist we look for a mounted filesystem (eg, EBS) and move the data_dir there. + +The client recipe is already included by server and 'default' recipes. + +For more infromation on the compile vs execution phase of a Chef run: + +- http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run + + +Chef Solo Note +-------------- +These node attributes are stored on the Chef server when using `chef-client`. Because `chef-solo` does not connect to a server or save the node object at all, to have the same passwords persist across `chef-solo` runs, you must specify them in the `json_attribs` file used. For example: + +```javascript +{ + "mysql": { + "server_root_password": "iloverandompasswordsbutthiswilldo", + "server_repl_password": "iloverandompasswordsbutthiswilldo", + "server_debian_password": "iloverandompasswordsbutthiswilldo" + }, + "run_list":["recipe[mysql::server]"] +} +``` + + +License & Authors +----------------- +- Author:: Joshua Timberman () +- Author:: AJ Christensen () +- Author:: Seth Chisamore () +- Author:: Brian Bianco () +- Author:: Jesse Howarth () +- Author:: Andrew Crump () + +```text +Copyright:: 2009-2013 Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/mysql/TESTING.md b/chef/cookbooks/mysql/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/mysql/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/mysql/attributes/client.rb b/chef/cookbooks/mysql/attributes/client.rb new file mode 100644 index 0000000..fcaa1da --- /dev/null +++ b/chef/cookbooks/mysql/attributes/client.rb @@ -0,0 +1,52 @@ +# +# Cookbook Name:: mysql +# Attributes:: client +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Include Opscode helper in Node class to get access +# to debian_before_squeeze? and ubuntu_before_lucid? +::Chef::Node.send(:include, Opscode::Mysql::Helpers) + +case node['platform_family'] +when "rhel", "fedora" + default['mysql']['client']['packages'] = %w{mysql mysql-devel} +when "suse" + default['mysql']['client']['packages'] = %w{mysql-community-server-client libmysqlclient-devel} +when "debian" + if debian_before_squeeze? || ubuntu_before_lucid? + default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient15-dev} + else + default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient-dev} + end +when "freebsd" + default['mysql']['client']['packages'] = %w{mysql55-client} +when "windows" + default['mysql']['client']['version'] = "6.0.2" + default['mysql']['client']['arch'] = "win32" # force 32 bit to work with mysql gem + default['mysql']['client']['package_file'] = "mysql-connector-c-#{mysql['client']['version']}-#{mysql['client']['arch']}.msi" + default['mysql']['client']['url'] = "http://www.mysql.com/get/Downloads/Connector-C/#{mysql['client']['package_file']}/from/http://mysql.mirrors.pair.com/" + default['mysql']['client']['packages'] = ["MySQL Connector C #{mysql['client']['version']}"] + + default['mysql']['client']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\Program Files (x86)\\MySQL\\#{mysql['client']['packages'].first}" + default['mysql']['client']['lib_dir'] = "#{mysql['client']['basedir']}\\lib/opt" + default['mysql']['client']['bin_dir'] = "#{mysql['client']['basedir']}\\bin" + default['mysql']['client']['ruby_dir'] = RbConfig::CONFIG['bindir'] +when "mac_os_x" + default['mysql']['client']['packages'] = %w{mysql-connector-c} +else + default['mysql']['client']['packages'] = %w{mysql-client libmysqlclient-dev} +end + diff --git a/chef/cookbooks/mysql/attributes/percona_repo.rb b/chef/cookbooks/mysql/attributes/percona_repo.rb new file mode 100644 index 0000000..80650a6 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/percona_repo.rb @@ -0,0 +1,3 @@ +default['mysql']['percona']['apt_key_id'] = 'CD2EFD2A' +default['mysql']['percona']['apt_uri'] = "http://repo.percona.com/apt" +default['mysql']['percona']['apt_keyserver'] = "keys.gnupg.net" diff --git a/chef/cookbooks/mysql/attributes/server.rb b/chef/cookbooks/mysql/attributes/server.rb new file mode 100644 index 0000000..6458c55 --- /dev/null +++ b/chef/cookbooks/mysql/attributes/server.rb @@ -0,0 +1,264 @@ +# +# Cookbook Name:: mysql +# Attributes:: server +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#if (defined? node['mycluster']) +# item = data_bag_item('openstack',node['mycluster']) +#else +# item = data_bag_item('openstack','env_default') +#end + +#binding_interface = item['network']['control']['interface'] +#file = File.open('/home/test.txt','w') +#file.write(binding_interface) + +default['mysql']['bind_address'] = node.attribute?('cloud') ? node.cloud['local_ipv4'] : node['ipaddress'] +default['mysql']['port'] = 3306 +default['mysql']['nice'] = 0 + +case node["platform_family"] +when "debian" + default['mysql']['server']['packages'] = %w{mysql-server} + default['mysql']['service_name'] = "mysql" + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + + default['mysql']['conf_dir'] = '/etc/mysql' + default['mysql']['confd_dir'] = '/etc/mysql/conf.d' + default['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" + default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + default['mysql']['old_passwords'] = 0 + default['mysql']['grants_path'] = "/etc/mysql/grants.sql" +when "rhel", "fedora" + if node["mysql"]["version"].to_f >= 5.5 + default['mysql']['service_name'] = "mysql" + default['mysql']['pid_file'] = "/var/run/mysql/mysql.pid" + else + default['mysql']['service_name'] = "mysqld" + default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + end + default['mysql']['server']['packages'] = %w{mysql-server} + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + + default['mysql']['conf_dir'] = '/etc' + default['mysql']['confd_dir'] = '/etc/mysql/conf.d' + default['mysql']['socket'] = "/var/lib/mysql/mysql.sock" + default['mysql']['old_passwords'] = 1 + default['mysql']['grants_path'] = "/etc/mysql_grants.sql" + # RHEL/CentOS mysql package does not support this option. + default['mysql']['tunable']['innodb_adaptive_flushing'] = false +when "suse" + default['mysql']['service_name'] = "mysql" + default['mysql']['server']['packages'] = %w{mysql-community-server} + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + default['mysql']['conf_dir'] = '/etc' + default['mysql']['confd_dir'] = '/etc/mysql/conf.d' + default['mysql']['socket'] = "/var/run/mysql/mysql.sock" + default['mysql']['pid_file'] = "/var/run/mysql/mysqld.pid" + default['mysql']['old_passwords'] = 1 + default['mysql']['grants_path'] = "/etc/mysql_grants.sql" +when "freebsd" + default['mysql']['server']['packages'] = %w{mysql55-server} + default['mysql']['service_name'] = "mysql-server" + default['mysql']['basedir'] = "/usr/local" + default['mysql']['data_dir'] = "/var/db/mysql" + default['mysql']['root_group'] = "wheel" + default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" + + default['mysql']['conf_dir'] = '/usr/local/etc' + default['mysql']['confd_dir'] = '/usr/local/etc/mysql/conf.d' + default['mysql']['socket'] = "/tmp/mysqld.sock" + default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + default['mysql']['old_passwords'] = 0 + default['mysql']['grants_path'] = "/var/db/mysql/grants.sql" +when "windows" + default['mysql']['server']['packages'] = ["MySQL Server 5.5"] + default['mysql']['version'] = '5.5.21' + default['mysql']['arch'] = 'win32' + default['mysql']['package_file'] = "mysql-#{mysql['version']}-#{mysql['arch']}.msi" + default['mysql']['url'] = "http://www.mysql.com/get/Downloads/MySQL-5.5/#{mysql['package_file']}/from/http://mysql.mirrors.pair.com/" + + default['mysql']['service_name'] = "mysql" + default['mysql']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\Program Files (x86)\\MySQL\\#{mysql['server']['packages'].first}" + default['mysql']['data_dir'] = "#{node['mysql']['basedir']}\\Data" + default['mysql']['bin_dir'] = "#{node['mysql']['basedir']}\\bin" + default['mysql']['mysqladmin_bin'] = "#{node['mysql']['bin_dir']}\\mysqladmin" + default['mysql']['mysql_bin'] = "#{node['mysql']['bin_dir']}\\mysql" + + default['mysql']['conf_dir'] = node['mysql']['basedir'] + default['mysql']['old_passwords'] = 0 + default['mysql']['grants_path'] = "#{node['mysql']['conf_dir']}\\grants.sql" +when "mac_os_x" + default['mysql']['server']['packages'] = %w{mysql} + default['mysql']['basedir'] = "/usr/local/Cellar" + default['mysql']['data_dir'] = "/usr/local/var/mysql" + default['mysql']['root_group'] = "admin" + default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" +else + default['mysql']['server']['packages'] = %w{mysql-server} + default['mysql']['service_name'] = "mysql" + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + + default['mysql']['conf_dir'] = '/etc/mysql' + default['mysql']['confd_dir'] = '/etc/mysql/conf.d' + default['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" + default['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + default['mysql']['old_passwords'] = 0 + default['mysql']['grants_path'] = "/etc/mysql/grants.sql" +end + +if attribute?('ec2') + default['mysql']['ec2_path'] = "/mnt/mysql" + default['mysql']['ebs_vol_dev'] = "/dev/sdi" + default['mysql']['ebs_vol_size'] = 50 +end + +default['mysql']['reload_action'] = "restart" # or "reload" or "none" + +default['mysql']['use_upstart'] = node['platform'] == "ubuntu" && node['platform_version'].to_f >= 10.04 + +default['mysql']['auto-increment-increment'] = 1 +default['mysql']['auto-increment-offset'] = 1 + +default['mysql']['allow_remote_root'] = true +default['mysql']['remove_anonymous_users'] = false +default['mysql']['remove_test_database'] = false +default['mysql']['root_network_acl'] = nil +default['mysql']['tunable']['character-set-server'] = "utf8" +default['mysql']['tunable']['collation-server'] = "utf8_general_ci" +default['mysql']['tunable']['lower_case_table_names'] = nil +default['mysql']['tunable']['back_log'] = "128" +default['mysql']['tunable']['key_buffer_size'] = "256M" +default['mysql']['tunable']['myisam_sort_buffer_size'] = "8M" +default['mysql']['tunable']['myisam_max_sort_file_size'] = "2147483648" +default['mysql']['tunable']['myisam_repair_threads'] = "1" +default['mysql']['tunable']['myisam-recover'] = "BACKUP" +default['mysql']['tunable']['max_allowed_packet'] = "16M" +default['mysql']['tunable']['max_connections'] = "800" +default['mysql']['tunable']['max_connect_errors'] = "10" +default['mysql']['tunable']['concurrent_insert'] = "2" +default['mysql']['tunable']['connect_timeout'] = "10" +default['mysql']['tunable']['tmp_table_size'] = "32M" +default['mysql']['tunable']['max_heap_table_size'] = node['mysql']['tunable']['tmp_table_size'] +default['mysql']['tunable']['bulk_insert_buffer_size'] = node['mysql']['tunable']['tmp_table_size'] +default['mysql']['tunable']['net_read_timeout'] = "30" +default['mysql']['tunable']['net_write_timeout'] = "30" +default['mysql']['tunable']['table_cache'] = "128" + +default['mysql']['tunable']['thread_cache_size'] = 8 +default['mysql']['tunable']['thread_concurrency'] = 10 +default['mysql']['tunable']['thread_stack'] = "256K" +default['mysql']['tunable']['sort_buffer_size'] = "2M" +default['mysql']['tunable']['read_buffer_size'] = "128k" +default['mysql']['tunable']['read_rnd_buffer_size'] = "256k" +default['mysql']['tunable']['join_buffer_size'] = "128k" +default['mysql']['tunable']['wait_timeout'] = "180" +default['mysql']['tunable']['open-files-limit'] = "1024" + +default['mysql']['tunable']['sql_mode'] = nil + +default['mysql']['tunable']['skip-character-set-client-handshake'] = false +default['mysql']['tunable']['skip-name-resolve'] = false + +default['mysql']['tunable']['slave_compressed_protocol'] = 0 + +default['mysql']['tunable']['server_id'] = nil +default['mysql']['tunable']['log_bin'] = nil +default['mysql']['tunable']['log_bin_trust_function_creators'] = false + +default['mysql']['tunable']['relay_log'] = nil +default['mysql']['tunable']['relay_log_index'] = nil +default['mysql']['tunable']['log_slave_updates'] = false + +default['mysql']['tunable']['sync_binlog'] = 0 +default['mysql']['tunable']['skip_slave_start'] = false +default['mysql']['tunable']['read_only'] = false + +default['mysql']['tunable']['log_error'] = nil +default['mysql']['tunable']['log_warnings'] = false +default['mysql']['tunable']['log_queries_not_using_index'] = true +default['mysql']['tunable']['log_bin_trust_function_creators'] = false + +default['mysql']['tunable']['innodb_log_file_size'] = "5M" +default['mysql']['tunable']['innodb_buffer_pool_size'] = "128M" +default['mysql']['tunable']['innodb_buffer_pool_instances'] = "4" +default['mysql']['tunable']['innodb_additional_mem_pool_size'] = "8M" +default['mysql']['tunable']['innodb_data_file_path'] = "ibdata1:10M:autoextend" +default['mysql']['tunable']['innodb_flush_method'] = false +default['mysql']['tunable']['innodb_log_buffer_size'] = "8M" +default['mysql']['tunable']['innodb_write_io_threads'] = "4" +default['mysql']['tunable']['innodb_io_capacity'] = "200" +default['mysql']['tunable']['innodb_file_per_table'] = true +default['mysql']['tunable']['innodb_lock_wait_timeout'] = "60" +if node['cpu'].nil? or node['cpu']['total'].nil? + default['mysql']['tunable']['innodb_thread_concurrency'] = "8" + default['mysql']['tunable']['innodb_commit_concurrency'] = "8" + default['mysql']['tunable']['innodb_read_io_threads'] = "8" +else + default['mysql']['tunable']['innodb_thread_concurrency'] = "#{(Integer(node['cpu']['total'])) * 2}" + default['mysql']['tunable']['innodb_commit_concurrency'] = "#{(Integer(node['cpu']['total'])) * 2}" + default['mysql']['tunable']['innodb_read_io_threads'] = "#{(Integer(node['cpu']['total'])) * 2}" +end +default['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = "1" +default['mysql']['tunable']['innodb_support_xa'] = true +default['mysql']['tunable']['innodb_table_locks'] = true +default['mysql']['tunable']['skip-innodb-doublewrite'] = false + +default['mysql']['tunable']['transaction-isolation'] = nil + +default['mysql']['tunable']['query_cache_limit'] = "1M" +default['mysql']['tunable']['query_cache_size'] = "16M" + +default['mysql']['tunable']['log_slow_queries'] = "/var/log/mysql/slow.log" +default['mysql']['tunable']['slow_query_log'] = node['mysql']['tunable']['log_slow_queries'] # log_slow_queries is deprecated + # in favor of slow_query_log +default['mysql']['tunable']['long_query_time'] = 2 + +default['mysql']['tunable']['expire_logs_days'] = 10 +default['mysql']['tunable']['max_binlog_size'] = "100M" +default['mysql']['tunable']['binlog_cache_size'] = "32K" + +default['mysql']['tmpdir'] = ["/tmp"] + +default['mysql']['log_dir'] = node['mysql']['data_dir'] +default['mysql']['log_files_in_group'] = false +default['mysql']['innodb_status_file'] = false + +unless node['platform_family'] == "rhel" && node['platform_version'].to_i < 6 + # older RHEL platforms don't support these options + default['mysql']['tunable']['event_scheduler'] = 0 + default['mysql']['tunable']['table_open_cache'] = "128" + default['mysql']['tunable']['binlog_format'] = "statement" if node['mysql']['tunable']['log_bin'] +end diff --git a/chef/cookbooks/mysql/libraries/helpers.rb b/chef/cookbooks/mysql/libraries/helpers.rb new file mode 100644 index 0000000..40adcfc --- /dev/null +++ b/chef/cookbooks/mysql/libraries/helpers.rb @@ -0,0 +1,33 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Opscode + module Mysql + module Helpers + + def debian_before_squeeze? + (node['platform'] == "debian") && (node['platform_version'].to_f < 6.0) + end + + def ubuntu_before_lucid? + (node['platform'] == "ubuntu") && (node['platform_version'].to_f < 10.0) + end + + end + end +end diff --git a/chef/cookbooks/mysql/metadata.rb b/chef/cookbooks/mysql/metadata.rb new file mode 100644 index 0000000..e47c693 --- /dev/null +++ b/chef/cookbooks/mysql/metadata.rb @@ -0,0 +1,140 @@ +name "mysql" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures mysql for client or server" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "3.0.5" +recipe "mysql", "Includes the client recipe to configure a client" +recipe "mysql::client", "Installs packages required for mysql clients using run_action magic" +recipe "mysql::server", "Installs packages required for mysql servers w/o manual intervention" +recipe "mysql::server_ec2", "Performs EC2-specific mountpoint manipulation" + +%w{ debian ubuntu centos suse fedora redhat scientific amazon freebsd windows mac_os_x }.each do |os| + supports os +end + +depends "openssl" +depends "build-essential", "> 1.1.0" +suggests "homebrew" +suggests "windows" + +attribute "mysql/server_root_password", + :display_name => "MySQL Server Root Password", + :description => "Randomly generated password for the mysqld root user", + :default => "randomly generated" + +attribute "mysql/bind_address", + :display_name => "MySQL Bind Address", + :description => "Address that mysqld should listen on", + :default => "ipaddress" + +attribute "mysql/data_dir", + :display_name => "MySQL Data Directory", + :description => "Location of mysql databases", + :default => "/var/lib/mysql" + +attribute "mysql/conf_dir", + :display_name => "MySQL Conf Directory", + :description => "Location of mysql conf files", + :default => "/etc/mysql" + +attribute "mysql/ec2_path", + :display_name => "MySQL EC2 Path", + :description => "Location of mysql directory on EC2 instance EBS volumes", + :default => "/mnt/mysql" + +attribute "mysql/reload_action", + :display_name => "MySQL conf file reload action", + :description => "Action to take when mysql conf files are modified", + :default => "reload" + +attribute "mysql/tunable", + :display_name => "MySQL Tunables", + :description => "Hash of MySQL tunable attributes", + :type => "hash" + +attribute "mysql/tunable/key_buffer", + :display_name => "MySQL Tuntable Key Buffer", + :default => "250M" + +attribute "mysql/tunable/max_connections", + :display_name => "MySQL Tunable Max Connections", + :default => "800" + +attribute "mysql/tunable/wait_timeout", + :display_name => "MySQL Tunable Wait Timeout", + :default => "180" + +attribute "mysql/tunable/net_read_timeout", + :display_name => "MySQL Tunable Net Read Timeout", + :default => "30" + +attribute "mysql/tunable/net_write_timeout", + :display_name => "MySQL Tunable Net Write Timeout", + :default => "30" + +attribute "mysql/tunable/back_log", + :display_name => "MySQL Tunable Back Log", + :default => "128" + +attribute "mysql/tunable/table_cache", + :display_name => "MySQL Tunable Table Cache for MySQL < 5.1.3", + :default => "128" + +attribute "mysql/tunable/table_open_cache", + :display_name => "MySQL Tunable Table Cache for MySQL >= 5.1.3", + :default => "128" + +attribute "mysql/tunable/max_heap_table_size", + :display_name => "MySQL Tunable Max Heap Table Size", + :default => "32M" + +attribute "mysql/tunable/expire_logs_days", + :display_name => "MySQL Exipre Log Days", + :default => "10" + +attribute "mysql/tunable/max_binlog_size", + :display_name => "MySQL Max Binlog Size", + :default => "100M" + +attribute "mysql/client", + :display_name => "MySQL Connector/C Client", + :description => "Hash of MySQL client attributes", + :type => "hash" + +attribute "mysql/client/version", + :display_name => "MySQL Connector/C Version", + :default => "6.0.2" + +attribute "mysql/client/arch", + :display_name => "MySQL Connector/C Architecture", + :default => "win32" + +attribute "mysql/client/package_file", + :display_name => "MySQL Connector/C Package File Name", + :default => "mysql-connector-c-6.0.2-win32.msi" + +attribute "mysql/client/url", + :display_name => "MySQL Connector/C Download URL", + :default => "http://www.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.0.2-win32.msi/from/http://mysql.mirrors.pair.com/" + +attribute "mysql/client/package_name", + :display_name => "MySQL Connector/C Registry DisplayName", + :default => "MySQL Connector C 6.0.2" + +attribute "mysql/client/basedir", + :display_name => "MySQL Connector/C Base Install Directory", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2" + +attribute "mysql/client/lib_dir", + :display_name => "MySQL Connector/C Library Directory (containing libmysql.dll)", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\lib\\opt" + +attribute "mysql/client/bin_dir", + :display_name => "MySQL Connector/C Executable Directory", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\bin" + +attribute "mysql/client/ruby_dir", + :display_name => "Ruby Executable Directory which should gain MySQL support", + :default => "system ruby" diff --git a/chef/cookbooks/mysql/recipes/client.rb b/chef/cookbooks/mysql/recipes/client.rb new file mode 100644 index 0000000..91787e7 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/client.rb @@ -0,0 +1,59 @@ +# +# Cookbook Name:: mysql +# Recipe:: client +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Include Opscode helper in Recipe class to get access +# to debian_before_squeeze? and ubuntu_before_lucid? +::Chef::Recipe.send(:include, Opscode::Mysql::Helpers) + +case node['platform'] +when "windows" + package_file = node['mysql']['client']['package_file'] + remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do + source node['mysql']['client']['url'] + not_if { File.exists? "#{Chef::Config[:file_cache_path]}/#{package_file}" } + end + + windows_package node['mysql']['client']['packages'].first do + source "#{Chef::Config[:file_cache_path]}/#{package_file}" + end + windows_path node['mysql']['client']['bin_dir'] do + action :add + end + def package(*args, &blk) + windows_package(*args, &blk) + end +when "mac_os_x" + include_recipe 'homebrew' +end + +node['mysql']['client']['packages'].each do |mysql_pack| + package mysql_pack do + action :install + end +end + +if platform? 'windows' + ruby_block "copy libmysql.dll into ruby path" do + block do + require 'fileutils' + FileUtils.cp "#{node['mysql']['client']['lib_dir']}\\libmysql.dll", node['mysql']['client']['ruby_dir'] + end + not_if { File.exist?("#{node['mysql']['client']['ruby_dir']}\\libmysql.dll") } + end +end diff --git a/chef/cookbooks/mysql/recipes/default.rb b/chef/cookbooks/mysql/recipes/default.rb new file mode 100644 index 0000000..9ff90d6 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "mysql::client" diff --git a/chef/cookbooks/mysql/recipes/percona_repo.rb b/chef/cookbooks/mysql/recipes/percona_repo.rb new file mode 100644 index 0000000..0051d68 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/percona_repo.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: mysql +# Recipe:: percona_repo +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +case node['platform'] +when "ubuntu", "debian" + include_recipe "apt" + apt_repository "percona" do + uri node['mysql']['percona']['apt_uri'] + distribution node['lsb']['codename'] + components [ "main" ] + keyserver node['mysql']['percona']['apt_keyserver'] + key node['mysql']['percona']['apt_key_id'] + action :add + end +when "centos", "amazon", "redhat" + include_recipe "yum" + yum_key "RPM-GPG-KEY-percona" do + url "http://www.percona.com/downloads/RPM-GPG-KEY-percona" + action :add + end + arch = node['kernel']['machine'] + arch = "i386" unless arch == "x86_64" + pversion = node['platform_version'].split('.').first + yum_repository "percona" do + repo_name "Percona" + description "Percona Repo" + url "http://repo.percona.com/centos/#{pversion}/os/#{arch}/" + key "RPM-GPG-KEY-percona" + action :add + end +end diff --git a/chef/cookbooks/mysql/recipes/ruby.rb b/chef/cookbooks/mysql/recipes/ruby.rb new file mode 100644 index 0000000..8c8470d --- /dev/null +++ b/chef/cookbooks/mysql/recipes/ruby.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: mysql +# Recipe:: ruby +# +# Author:: Jesse Howarth () +# Author:: Jamie Winsor () +# +# Copyright 2008-2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +node.set['build_essential']['compiletime'] = true +include_recipe "build-essential" +include_recipe "mysql::client" + +node['mysql']['client']['packages'].each do |mysql_pack| + resources("package[#{mysql_pack}]").run_action(:install) +end + +chef_gem "mysql" diff --git a/chef/cookbooks/mysql/recipes/server.rb b/chef/cookbooks/mysql/recipes/server.rb new file mode 100644 index 0000000..8a285e8 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/server.rb @@ -0,0 +1,228 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) + +include_recipe "mysql::client" + +if Chef::Config[:solo] + missing_attrs = %w{ + server_debian_password server_root_password server_repl_password + }.select do |attr| + node["mysql"][attr].nil? + end.map { |attr| "node['mysql']['#{attr}']" } + + if !missing_attrs.empty? + Chef::Application.fatal!([ + "You must set #{missing_attrs.join(', ')} in chef-solo mode.", + "For more information, see https://github.com/opscode-cookbooks/mysql#chef-solo-note" + ].join(' ')) + end +else + # generate all passwords + # node.override['mysql']['server_debian_password'] = mydata['credential']['mysql']['super']['password'] + # node.override['mysql']['server_root_password'] = mydata['credential']['mysql']['super']['password'] + # node.override['mysql']['server_repl_password'] = mydata['credential']['mysql']['super']['password'] + # node.set_unless['mysql']['server_debian_password'] = secure_password + # node.set_unless['mysql']['server_root_password'] = item['credential']['service_credential']['password'] + # node.set_unless['mysql']['server_repl_password'] = item['credential']['service_credential']['password'] + # node.save + # puts "---------------else generate all passwords-----------------------------" + # puts "['mysql']['server_debian_password']= #{node.['mysql']['server_debian_password']}" + # puts "['mysql']['server_root_password']=#{node.['mysql']['server_root_password']}" + # puts "-----------------------------------------------------------------------" +end + +if platform_family?(%w{debian}) + + directory "/var/cache/local/preseeding" do + owner "root" + group node['mysql']['root_group'] + mode 0755 + recursive true + end + + execute "preseed mysql-server" do + command "debconf-set-selections /var/cache/local/preseeding/mysql-server.seed" + action :nothing + end + + template "/var/cache/local/preseeding/mysql-server.seed" do + source "mysql-server.seed.erb" + owner "root" + group node['mysql']['root_group'] + mode "0600" + notifies :run, "execute[preseed mysql-server]", :immediately + end + + template "#{node['mysql']['conf_dir']}/debian.cnf" do + source "debian.cnf.erb" + owner "root" + group node['mysql']['root_group'] + mode "0600" + end + +end + +if platform_family?('windows') + package_file = node['mysql']['package_file'] + + remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do + source node['mysql']['url'] + not_if { File.exists? "#{Chef::Config[:file_cache_path]}/#{package_file}" } + end + + windows_package node['mysql']['server']['packages'].first do + source "#{Chef::Config[:file_cache_path]}/#{package_file}" + end + + def package(*args, &blk) + windows_package(*args, &blk) + end +end + +node['mysql']['server']['packages'].each do |package_name| + package package_name do + action :install + notifies :start, "service[mysql]", :immediately + end +end + +unless platform_family?(%w{mac_os_x}) + + [File.dirname(node['mysql']['pid_file']), + File.dirname(node['mysql']['tunable']['slow_query_log']), + node['mysql']['conf_dir'], + node['mysql']['confd_dir'], + node['mysql']['log_dir'], + node['mysql']['data_dir']].each do |directory_path| + directory directory_path do + owner "mysql" unless platform? 'windows' + group "mysql" unless platform? 'windows' + action :create + recursive true + end + end + + if platform_family? 'windows' + require 'win32/service' + + windows_path node['mysql']['bin_dir'] do + action :add + end + + windows_batch "install mysql service" do + command "\"#{node['mysql']['bin_dir']}\\mysqld.exe\" --install #{node['mysql']['service_name']}" + not_if { Win32::Service.exists?(node['mysql']['service_name']) } + end + end + + skip_federated = case node['platform'] + when 'fedora', 'ubuntu', 'amazon' + true + when 'centos', 'redhat', 'scientific' + node['platform_version'].to_f < 6.0 + else + false + end +end + +# Homebrew has its own way to do databases +if platform_family?(%w{mac_os_x}) + execute "mysql-install-db" do + command "mysql_install_db --verbose --user=`whoami` --basedir=\"$(brew --prefix mysql)\" --datadir=#{node['mysql']['data_dir']} --tmpdir=/tmp" + environment('TMPDIR' => nil) + action :run + creates "#{node['mysql']['data_dir']}/mysql" + end +else + execute 'mysql-install-db' do + command "mysql_install_db" + action :run + not_if { File.exists?(node['mysql']['data_dir'] + '/mysql/user.frm') } + end + + service "mysql" do + service_name node['mysql']['service_name'] + if node['mysql']['use_upstart'] + provider Chef::Provider::Service::Upstart + end + supports :status => true, :restart => true, :reload => true + action :enable + end +end + +# set the root password for situations that don't support pre-seeding. +# (eg. platforms other than debian/ubuntu & drop-in mysql replacements) +execute "assign-root-password" do + command %Q["#{node['mysql']['mysqladmin_bin']}" -u root password '#{node['mysql']['server_root_password']}'] + action :run + only_if %Q["#{node['mysql']['mysql_bin']}" -u root -e 'show databases;'] +end + +unless platform_family?(%w{mac_os_x}) + grants_path = node['mysql']['grants_path'] + + begin + t = resources("template[#{grants_path}]") + rescue + Chef::Log.info("Could not find previously defined grants.sql resource") + t = template grants_path do + source "grants.sql.erb" + owner "root" unless platform_family? 'windows' + group node['mysql']['root_group'] unless platform_family? 'windows' + mode "0600" + action :create + end + end + + if platform_family? 'windows' + windows_batch "mysql-install-privileges" do + command "\"#{node['mysql']['mysql_bin']}\" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }\"#{node['mysql']['server_root_password']}\" < \"#{grants_path}\"" + action :nothing + subscribes :run, resources("template[#{grants_path}]"), :immediately + end + else + execute "mysql-install-privileges" do + command %Q["#{node['mysql']['mysql_bin']}" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }'#{node['mysql']['server_root_password']}' < "#{grants_path}"] + action :nothing + subscribes :run, resources("template[#{grants_path}]"), :immediately + end + end + + template "#{node['mysql']['conf_dir']}/my.cnf" do + source "my.cnf.erb" + owner "root" unless platform? 'windows' + group node['mysql']['root_group'] unless platform? 'windows' + mode "0644" + case node['mysql']['reload_action'] + when 'restart' + notifies :restart, "service[mysql]", :immediately + when 'reload' + notifies :reload, "service[mysql]", :immediately + else + Chef::Log.info "my.cnf updated but mysql.reload_action is #{node['mysql']['reload_action']}. No action taken." + end + variables :skip_federated => skip_federated + end + + service "mysql" do + action :start + end +end diff --git a/chef/cookbooks/mysql/recipes/server_ec2.rb b/chef/cookbooks/mysql/recipes/server_ec2.rb new file mode 100644 index 0000000..6033ef4 --- /dev/null +++ b/chef/cookbooks/mysql/recipes/server_ec2.rb @@ -0,0 +1,51 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +if (node.attribute?('ec2') && ! FileTest.directory?(node['mysql']['ec2_path'])) + + service "mysql" do + action :stop + end + + execute "install-mysql" do + command "mv #{node['mysql']['data_dir']} #{node['mysql']['ec2_path']}" + not_if do FileTest.directory?(node['mysql']['ec2_path']) end + end + + [node['mysql']['ec2_path'], node['mysql']['data_dir']].each do |dir| + directory dir do + owner "mysql" + group "mysql" + end + end + + mount node['mysql']['data_dir'] do + device node['mysql']['ec2_path'] + fstype "none" + options "bind,rw" + action [:mount, :enable] + end + + service "mysql" do + action :start + end + +end + diff --git a/chef/cookbooks/mysql/templates/default/debian.cnf.erb b/chef/cookbooks/mysql/templates/default/debian.cnf.erb new file mode 100644 index 0000000..989b125 --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/debian.cnf.erb @@ -0,0 +1,12 @@ +[client] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> + +[mysql_upgrade] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> +basedir = /usr diff --git a/chef/cookbooks/mysql/templates/default/grants.sql.erb b/chef/cookbooks/mysql/templates/default/grants.sql.erb new file mode 100644 index 0000000..87c0a0c --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/grants.sql.erb @@ -0,0 +1,40 @@ +# Generated by Chef for <%= node['hostname'] %>. +# Local modifications will be overwritten. +<% case node['platform_family'] -%> +<% when "debian" -%> + +# Grant privileges for debian-sys-main user +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '<%= node['mysql']['server_debian_password'] %>' WITH GRANT OPTION; +<% end %> + +# Grant replication for a slave user. +GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' identified by '<%= node['mysql']['server_repl_password'] %>'; +<% if node['mysql']['allow_remote_root'] -%> + +# Set the server root password. This should be preseeded by the package installation. +GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '<%= node['mysql']['server_root_password'] %>' WITH GRANT OPTION; +<% else %> + +# remove remote access for root user and set password for local root user +DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); +UPDATE mysql.user SET Password=PASSWORD('<%= node['mysql']['server_root_password'] %>') WHERE User='root'; +<% end %> +<% if node['mysql']['remove_anonymous_users'] -%> + +# Remove anonymous users +DELETE FROM mysql.user WHERE User=''; +<% end %> +<% if node['mysql']['remove_test_database'] -%> + +# Remove test database and access to it +DROP DATABASE IF EXISTS test; +DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'; +<% end %> + +# Set the password for root@localhost +SET PASSWORD FOR 'root'@'localhost' = PASSWORD('<%= node['mysql']['server_root_password'] %>'); +<% if node['mysql']['root_network_acl'] -%> + +# allow root to connect from a remote network if root_network_acl is not nil +GRANT ALL PRIVILEGES ON *.* TO 'root'@'<%= node['mysql']['root_network_acl'] %>' IDENTIFIED BY '<%= node['mysql']['server_root_password'] %>' WITH GRANT OPTION; +<% end -%> diff --git a/chef/cookbooks/mysql/templates/default/my.cnf.erb b/chef/cookbooks/mysql/templates/default/my.cnf.erb new file mode 100644 index 0000000..669d060 --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/my.cnf.erb @@ -0,0 +1,315 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# You can copy this to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +# Remember to edit /etc/mysql/debian.cnf when changing the socket location. +[client] +port = <%= node['mysql']['port'] %> +socket = <%= node['mysql']['socket'] %> + +# Here is entries for some specific programs +# The following values assume you have at least 32M ram + +# This was formally known as [safe_mysqld]. Both versions are currently parsed. +[mysqld_safe] +socket = <%= node['mysql']['socket'] %> +nice = <%= node['mysql']['nice'] %> + +[mysqld] +# +# * Basic Settings +# + +# +# * IMPORTANT +# If you make changes to these settings and your system uses apparmor, you may +# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. +# + +user = mysql +pid-file = <%= node['mysql']['pid_file'] %> +socket = <%= node['mysql']['socket'] %> +port = <%= node['mysql']['port'] %> +basedir = <%= node['mysql']['basedir'] %> +datadir = <%= node['mysql']['data_dir'] %> +tmpdir = <%= node['mysql']['tmpdir'].join(':') %> +skip-external-locking +<%- if node['mysql']['tunable']['skip-name-resolve'] %> +skip-name-resolve +<%- end %> + +# Charset and Collation +character-set-server = <%= node['mysql']['tunable']['character-set-server'] %> +collation-server = <%= node['mysql']['tunable']['collation-server'] %> +<%- if node['mysql']['tunable']['lower_case_table_names'] %> +lower_case_table_names = <%= node['mysql']['tunable']['lower_case_table_names'] %> +<%- end %> +<%- if node['mysql']['tunable']['event_scheduler'] %> +event_scheduler = <%= node['mysql']['tunable']['event_scheduler'] %> +<%- end %> +<%- if node['mysql']['tunable']['skip-character-set-client-handshake'] %> +skip-character-set-client-handshake +<%- end %> +<%- if (node['mysql']['tunable']['lc_messages_dir'] && node['mysql']['tunable']['lc_messages']) %> +lc_messages_dir = <%= node['mysql']['lc_messages_dir'] %> +lc_messages = <%= node['mysql']['lc_messages'] %> +<%- elsif (node['mysql']['tunable']['languages']) %> +languages = <%= node['mysql']['tunable']['languages'] %> +<%- end %> + +# +# Instead of skip-networking the default is now to listen only on +# localhost which is more compatible and is not less secure. +bind-address = <%= node['mysql']['bind_address'] %> +# +# * Fine Tuning +# +key_buffer_size = <%= node['mysql']['tunable']['key_buffer_size'] %> +max_allowed_packet = <%= node['mysql']['tunable']['max_allowed_packet'] %> +thread_stack = <%= node['mysql']['tunable']['thread_stack'] %> +thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> +sort_buffer_size = <%= node['mysql']['tunable']['sort_buffer_size'] %> +read_buffer_size = <%= node['mysql']['tunable']['read_buffer_size'] %> +read_rnd_buffer_size = <%= node['mysql']['tunable']['read_rnd_buffer_size'] %> +join_buffer_size = <%= node['mysql']['tunable']['join_buffer_size'] %> + +auto-increment-increment = <%= node['mysql']['auto-increment-increment'] %> +auto-increment-offset = <%= node['mysql']['auto-increment-offset'] %> + +# This replaces the startup script and checks MyISAM tables if needed +# the first time they are touched +myisam-recover = <%= node['mysql']['tunable']['myisam-recover'] %> +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +max_connect_errors = <%= node['mysql']['tunable']['max_connect_errors'] %> +concurrent_insert = <%= node['mysql']['tunable']['concurrent_insert'] %> +connect_timeout = <%= node['mysql']['tunable']['connect_timeout'] %> +wait_timeout = <%= node['mysql']['tunable']['wait_timeout'] %> +net_read_timeout = <%= node['mysql']['tunable']['net_read_timeout'] %> +net_write_timeout = <%= node['mysql']['tunable']['net_write_timeout'] %> +back_log = <%= node['mysql']['tunable']['back_log'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +<%- if node['mysql']['tunable']['table_open_cache'] %> +table_open_cache = <%= node['mysql']['tunable']['table_open_cache'] %> +<%- end %> +tmp_table_size = <%= node['mysql']['tunable']['tmp_table_size'] %> +max_heap_table_size = <%= node['mysql']['tunable']['max_heap_table_size'] %> +bulk_insert_buffer_size = <%= node['mysql']['tunable']['bulk_insert_buffer_size'] %> +open-files-limit = <%= node['mysql']['tunable']['open-files-limit'] %> + +# Default Table Settings +<%- if node['mysql']['tunable']['sql_mode'] %> +sql_mode = "<%= node['mysql']['tunable']['sql_mode'] %>" +<%- end %> + +# +# * Query Cache Configuration +# +query_cache_limit = <%= node['mysql']['tunable']['query_cache_limit'] %> +query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> +# +# * Logging +# +# Both location gets rotated by the cronjob. +# Be aware that this log type is a performance killer. +#log = /var/log/mysql/mysql.log +# +# Error logging goes to syslog. This is a Debian improvement :) +<%- if node['mysql']['tunable']['log_error'] %> +log_error = <%= node['mysql']['tunable']['log_error'] %> +<%- end %> +<%- if node['mysql']['tunable']['log_warnings'] %> +log_warnings +<%- end %> +# +# * Replication +# + + +# +# Here you can see queries with especially long duration +<%- if node['mysql']['version'].to_f >= 5.5 %> +slow_query_log = <%= node['mysql']['tunable']['slow_query_log'] %> +<% else %> +log_slow_queries = <%= node['mysql']['tunable']['slow_query_log'] %> +<% end %> + +long_query_time = <%= node['mysql']['tunable']['long_query_time'] %> +<%- if node['mysql']['tunable']['log_queries_not_using_index'] and node['mysql']['slow_query_log'] %> +log-queries-not-using-indexes +<%- end %> +# +# The following can be used as easy to replay backup logs or for replication. +# note: if you are setting up a replication slave, see README.Debian about +# other settings you may need to change. +<%- if node['mysql']['tunable']['server_id'] %> +server-id = <%= node['mysql']['tunable']['server_id'] %> +<% end %> +<%- if node['mysql']['tunable']['log_bin'] %> +log_bin = <%= node['mysql']['tunable']['log_bin'] %> +binlog_format = <%= node['mysql']['tunable']['binlog_format'] %> +log_slave_updates = <%= node['mysql']['tunable']['log_slave_updates'] %> +<%- end %> +<%- if node['mysql']['tunable']['log_bin_trust_function_creators'] %> +log_bin_trust_function_creators +<%- end %> +expire_logs_days = <%= node['mysql']['tunable']['expire_logs_days'] %> +max_binlog_size = <%= node['mysql']['tunable']['max_binlog_size'] %> +binlog_cache_size = <%= node['mysql']['tunable']['binlog_cache_size'] %> +#binlog_do_db = include_database_name +#binlog_ignore_db = include_database_name +<%- if node['mysql']['tunable']['relay_log'] %> +relay_log = <%= node['mysql']['tunable']['relay_log'] %> +<%- end %> +<%- if node['mysql']['tunable']['relay_log_index'] %> +relay_log_index = <%= node['mysql']['tunable']['relay_log_index'] %> +<%- end %> + +sync_binlog = <%= node['mysql']['tunable']['sync_binlog'] %> +<%- if node['mysql']['tunable']['skip_slave_start'] %> +skip_slave_start +<%- end %> +<%- if node['mysql']['tunable']['read_only'] %> +read_only = 1 +<%- end %> + +<%- if node['mysql']['tunable']['transaction-isolation'] %> +transaction-isolation = <%= node['mysql']['tunable']['transaction-isolation'] %> +<%- end %> + +<%- if node['mysql']['tunable']['slave_compressed_protocol'] %> +slave_compressed_protocol = <%= node['mysql']['tunable']['slave_compressed_protocol'] %> +<%- end %> +# +# * InnoDB +# +# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +#skip-innodb + +<%- if node["mysql"]["version"].to_f >= 5.5 %> +innodb_write_io_threads = <%= node['mysql']['tunable']['innodb_write_io_threads'] %> +innodb_io_capacity = <%= node['mysql']['tunable']['innodb_io_capacity'] %> +innodb_read_io_threads = <%= node['mysql']['tunable']['innodb_read_io_threads'] %> +innodb_buffer_pool_instances = <%= node['mysql']['tunable']['innodb_buffer_pool_instances'] %> +<%- end %> + +## InnoDB Plugin Independent Settings +innodb_data_home_dir = <%= node['mysql']['data_dir'] %> +innodb_log_group_home_dir = <%= node['mysql']['log_dir'] %> +<%- if node['mysql']['log_files_in_group'] %> +innodb_log_files_in_group = <%= node['mysql']['log_files_in_group'] %> +<%- end %> + +<%- if node['mysql']['innodb_status_file'] %> +innodb_status_file +<%- end %> +<%- if node['mysql']['tunable']['innodb_file_per_table'] %> +innodb_file_per_table +<%- end %> +innodb_table_locks = <%= node['mysql']['tunable']['innodb_table_locks'] %> +innodb_lock_wait_timeout = <%= node['mysql']['tunable']['innodb_lock_wait_timeout'] %> +innodb_thread_concurrency = <%= node['mysql']['tunable']['innodb_thread_concurrency'] %> +innodb_commit_concurrency = <%= node['mysql']['tunable']['innodb_commit_concurrency'] %> +innodb_support_xa = <%= node['mysql']['tunable']['innodb_support_xa'] %> +<%- if node['mysql']['tunable']['skip-innodb-doublewrite'] %> +skip-innodb-doublewrite +<%- end %> + +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +innodb_log_file_size = <%= node['mysql']['tunable']['innodb_log_file_size'] %> +innodb_additional_mem_pool_size = <%= node['mysql']['tunable']['innodb_additional_mem_pool_size'] %> +innodb_data_file_path = <%= node['mysql']['tunable']['innodb_data_file_path'] %> +innodb_flush_log_at_trx_commit = <%= node['mysql']['tunable']['innodb_flush_log_at_trx_commit'] %> +<%- if node['mysql']['tunable']['innodb_flush_method'] %> +innodb_flush_method = <%= node['mysql']['tunable']['innodb_flush_method'] %> +<%- end %> +innodb_log_buffer_size = <%= node['mysql']['tunable']['innodb_log_buffer_size'] %> +<%- if node['mysql']['tunable']['innodb_adaptive_flushing'] %> +innodb_adaptive_flushing = <%= node['mysql']['tunable']['innodb_adaptive_flushing'] %> +<%- end %> + +<% if @skip_federated %> +# +# * Federated +# +# The FEDERATED storage engine is disabled since 5.0.67 by default in the .cnf files +# shipped with MySQL distributions (my-huge.cnf, my-medium.cnf, and so forth). +# +skip-federated +<% end %> +# +# * Security Features +# +# Read the manual, too, if you want chroot! +# chroot = /var/lib/mysql/ +# +# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". +# +# ssl-ca=/etc/mysql/cacert.pem +# ssl-cert=/etc/mysql/server-cert.pem +# ssl-key=/etc/mysql/server-key.pem + +[mysqldump] +quick +quote-names +max_allowed_packet = <%= node['mysql']['tunable']['max_allowed_packet'] %> + +[mysql] +#no-auto-rehash # faster start of mysql but no tab completition + +[myisamchk] +key_buffer = <%= node['mysql']['tunable']['max_allowed_packet'] %> + +myisam_sort_buffer_size = <%= node['mysql']['tunable']['myisam_sort_buffer_size'] %> +myisam_max_sort_file_size = <%= node['mysql']['tunable']['myisam_max_sort_file_size'] %> +myisam_repair_threads = <%= node['mysql']['tunable']['myisam_repair_threads'] %> +myisam-recover = <%= node['mysql']['tunable']['myisam-recover'] %> + +# +# * NDB Cluster +# +# See /usr/share/doc/mysql-server-*/README.Debian for more information. +# +# The following configuration is read by the NDB Data Nodes (ndbd processes) +# not from the NDB Management Nodes (ndb_mgmd processes). +# +# [MYSQL_CLUSTER] +# ndb-connectstring=127.0.0.1 + +<% case node['platform_family'] -%> +<% when "rhel", "fedora", "suse" -%> +# +# * BerkeleyDB +# +# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12. +skip-bdb +# Default to using old password format for compatibility with mysql 3.x +# clients (those using the mysqlclient10 compatibility package). +old_passwords = <%= node['mysql']['old_passwords'] %> +<% end -%> + +<% if node['mysql']['confd_dir'] -%> +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +!includedir <%= node['mysql']['confd_dir'] %>/ +<% end -%> diff --git a/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb b/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb new file mode 100644 index 0000000..a5a74f0 --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb @@ -0,0 +1,10 @@ +mysql-server-5.0 mysql-server/root_password_again select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server/root_password select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server-5.0/really_downgrade boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat boolean false +mysql-server-5.0 mysql-server-5.0/start_on_boot boolean true +mysql-server-5.0 mysql-server/error_setting_password boolean false +mysql-server-5.0 mysql-server-5.0/nis_warning note +mysql-server-5.0 mysql-server-5.0/postrm_remove_databases boolean false +mysql-server-5.0 mysql-server/password_mismatch boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat_done boolean true diff --git a/chef/cookbooks/mysql/templates/default/port_mysql.erb b/chef/cookbooks/mysql/templates/default/port_mysql.erb new file mode 100644 index 0000000..8ad63a7 --- /dev/null +++ b/chef/cookbooks/mysql/templates/default/port_mysql.erb @@ -0,0 +1,3 @@ +# MySQL +-A FWR -p tcp -m tcp --dport 3306 -j ACCEPT +-A FWR -p udp -m udp --dport 3306 -j ACCEPT diff --git a/chef/cookbooks/mysql/templates/windows/my.cnf.erb b/chef/cookbooks/mysql/templates/windows/my.cnf.erb new file mode 100644 index 0000000..f0550c1 --- /dev/null +++ b/chef/cookbooks/mysql/templates/windows/my.cnf.erb @@ -0,0 +1,61 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +[client] +port = 3306 + +[mysql] +default-character-set = latin1 + +[mysqld] +# +# * Basic Settings +# +port = 3306 +basedir = <%= node['mysql']['basedir'] %> +datadir = <%= node['mysql']['data_dir'] %> +character-set-server = latin1 +default-storage-engine = INNODB +sql-mode = "STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" + +# +# * Fine Tuning +# +thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> +tmp_table_size = 5M +myisam_max_sort_file_size = 100G +myisam_sort_buffer_size = 8M +key_buffer_size = 8M +read_buffer_size = 64K +read_rnd_buffer_size = 256K +sort_buffer_size = 212K + +# +# * InnoDB +# +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +# +innodb_additional_mem_pool_size = 2M +innodb_flush_log_at_trx_commit = 1 +innodb_log_buffer_size = 1M +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +innodb_log_file_size = 10M +innodb_thread_concurrency = 8 diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md b/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md new file mode 100644 index 0000000..d710160 --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/README.md @@ -0,0 +1,63 @@ +Description +=========== + +This cookbook defines acceptance tests for MySQL. It includes: + +* A `features` sub-directory where the Cucumber features for the database + are defined. + +* Creation of a simple test database for the tests to run against. + +Usage +===== + +Set environment variable `TEST_SERVER_HOST` to specify the MySQL server to +connect to. You can optionally set `TEST_CLIENT_HOST` which will test a client +install by running the same features from a remote client. + +Requirements +============ + +## Cookbooks: + +This cookbook depends on the `mysql` cookbook. It also uses the `database` +cookbook to create the test database and relies on the `yum` cookbook in order +to add the EPEL repository on RHEL-derived distributions. + +## Platforms: + +* Ubuntu +* CentOS + +Attributes +========== + +* `node['mysql_test']['database']` - The name of the test database to create. +* `node['mysql_test']['username']` - The username of the datbase user. +* `node['mysql_test']['password']` - The password of the database user. + +Recipes +======= + +* `client` - Simply includes `mysql::client` for a vanilla mysql client install. +* `server` - Includes `mysql::server` to install the server and configures a + test database. + +License and Authors +=================== + +Author:: Andrew Crump + + Copyright:: 2012, Opscode, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb new file mode 100644 index 0000000..b2dbc85 --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/attributes/default.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: mysql_test +# Attributes:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Must be specified for chef-solo for successful re-converge +override['mysql']['server_root_password'] = 'e;br$ilRoj7' + +default['mysql_test']['database'] = 'mysql_test' +default['mysql_test']['username'] = 'test_user' +default['mysql_test']['password'] = 'neshFiapog' + +override['mysql']['bind_address'] = 'localhost' diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb new file mode 100644 index 0000000..2850708 --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/server_test.rb @@ -0,0 +1,36 @@ +require File.expand_path('../support/helpers.rb', __FILE__) + +describe 'mysql::server' do + + include Helpers::Mysql + + it 'has a secure operating system password' do + assert_secure_password(:debian) + end + it 'has a secure root password' do + assert_secure_password(:root) + end + it 'has a secure replication password' do + assert_secure_password(:repl) + end + it 'installs the mysql packages' do + node['mysql']['server']['packages'].each do |package_name| + package(package_name).must_be_installed + end + end + it 'has a config directory' do + directory(node['mysql']['confd_dir']).must_exist.with(:owner, 'mysql').and(:group, 'mysql') + end + it 'runs as a daemon' do + service(node['mysql']['service_name']).must_be_running + end + it 'creates a my.cnf' do + file("#{node['mysql']['conf_dir']}/my.cnf").must_exist + end + describe 'debian' do + it 'creates a config file for service control' do + skip unless ['debian'].include?(node['platform_family']) + file("#{node['mysql']['conf_dir']}/debian.cnf").must_exist + end + end +end diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..c8b3fa2 --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,11 @@ +module Helpers + module Mysql + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + def assert_secure_password(type) + node["mysql"]["server_#{type}_password"].length.must_be_close_to(20, 8) + end + end +end diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb new file mode 100644 index 0000000..0a66cbd --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Andrew Crump" +maintainer_email "andrew@kotirisoftware.com" +license "Apache 2.0" +description "Acceptance tests for mysql" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.1.0" + +depends "database" +depends "yum" diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb new file mode 100644 index 0000000..235a67c --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/client.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: mysql_test +# Recipe:: client +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "yum::epel" if platform_family?('rhel') diff --git a/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb b/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb new file mode 100644 index 0000000..4b792ee --- /dev/null +++ b/chef/cookbooks/mysql/test/cookbooks/mysql_test/recipes/server.rb @@ -0,0 +1,62 @@ +# +# Cookbook Name:: mysql_test +# Recipe:: server +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +node.set['mysql']['server_debian_password'] = "ilikerandompasswords" +node.set['mysql']['server_root_password'] = "ilikerandompasswords" +node.set['mysql']['server_repl_password'] = "ilikerandompasswords" + +include_recipe "mysql::ruby" +include_recipe "yum::epel" if platform_family?('rhel') + +file "/etc/sysconfig/network" do + content "NETWORKING=yes" + action :create_if_missing + only_if { platform_family?('rhel', 'fedora') } +end + +include_recipe 'mysql::server' + +mysql_connection = {:host => "localhost", :username => 'root', + :password => node['mysql']['server_root_password']} + +mysql_database node['mysql_test']['database'] do + connection mysql_connection + action :create +end + +mysql_database_user node['mysql_test']['username'] do + connection mysql_connection + password node['mysql_test']['password'] + database_name node['mysql_test']['database'] + host 'localhost' + privileges [:select,:update,:insert, :delete] + action [:create, :grant] +end + +mysql_conn_args = "--user=root --password='#{node['mysql']['server_root_password']}'" + +execute 'create-sample-data' do + command %Q{mysql #{mysql_conn_args} #{node['mysql_test']['database']} < 1.7.0' +cookbook 'build-essential' diff --git a/chef/cookbooks/nodejs/CHANGELOG.md b/chef/cookbooks/nodejs/CHANGELOG.md new file mode 100644 index 0000000..54701f4 --- /dev/null +++ b/chef/cookbooks/nodejs/CHANGELOG.md @@ -0,0 +1,56 @@ +## v1.3.0 + * update default versions to the latest: node - v0.10.15 and npm - v1.3.5 + * default to package installation of nodejs on smartos ([@wanelo-pair][]) + * Add Raspberry pi support ([@robertkowalski][]) + +## v1.2.0 + * implement installation from package on RedHat - ([@vaskas][]) + +## v1.1.3: + * update default version of node to 0.10.13 - and npm - v1.3.4 ([@jodosha][]) + +## v1.1.2: + * update default version of node to 0.10.2 - ([@bakins][]) + * fully migrated to test-kitchen 1.alpha and vagrant 1.1.x/berkshelf 1.3.1 + +## v1.1.1: + * update default versions to the latest: node - v0.10.0 and npm - v1.2.14 + * `make_thread` is now a real attribute - ([@ChrisLundquist][]) + + +## v1.1.0: + * rewrite the package install; remove rpm support since there are no longer any packages available anywhere + * add support to install `legacy_packages` from ubuntu repo as well as the latest 0.10.x branch (this is default). + +## v1.0.4: + * add support for binary installation method ([@JulesAU][]) + +## v1.0.3: + - unreleased + +## v1.0.2: + * add smartos support for package install ([@sax][]) + * support to compile with all processors available (default 2 if unknown) - ([@ChrisLundquist][]) + * moved to `platform_family` syntax + * ensure npm recipe honours the 'source' or 'package' setting - ([@markbirbeck][]) + * updated the default versions to the latest stable node/npm + +## v1.0.1: + + * fixed bug that prevented overwritting the node/npm versions (moved the `src_url`s as local variables instead of attributes) - ([@johannesbecker][]) + * updated the default versions to the latest node/npm + +## v1.0.0: + +* added packages installation support ([@smith][]) + +[@JulesAU]: https://github.com/JulesAU +[@sax]: https://github.com/sax +[@ChrisLundquist]: https://github.com/ChrisLundquist +[@markbirbeck]: https://github.com/markbirbeck +[@johannesbecker]: https://github.com/johannesbecker +[@smith]: https://github.com/smith +[@bakins]: https://github.com/bakins +[@vaskas]: https://github.com/vaskas +[@robertkowalski]: https://github.com/robertkowalski +[@wanelo-pair]: https://github.com/wanelo-pair diff --git a/chef/cookbooks/nodejs/Gemfile b/chef/cookbooks/nodejs/Gemfile new file mode 100644 index 0000000..5840424 --- /dev/null +++ b/chef/cookbooks/nodejs/Gemfile @@ -0,0 +1,10 @@ +source 'https://rubygems.org' + +gem 'foodcritic' +gem 'thor-foodcritic' + +group :integration do + gem 'berkshelf' + gem 'test-kitchen', '~> 1.0.0.beta' + gem 'kitchen-vagrant', '~> 0.11.0' +end diff --git a/chef/cookbooks/nodejs/README.md b/chef/cookbooks/nodejs/README.md new file mode 100644 index 0000000..3fcd7d1 --- /dev/null +++ b/chef/cookbooks/nodejs/README.md @@ -0,0 +1,80 @@ +# nodejs-cookbook [![Build Status](https://secure.travis-ci.org/mdxp/nodejs-cookbook.png)](http://travis-ci.org/mdxp/nodejs-cookbook) + +DESCRIPTION +=========== + +Installs Node.JS + +REQUIREMENTS +============ + + +## Platform + +* Tested on Debian 6 and Ubuntu 10.04 +* Should work fine on Centos, RHEL, etc. + +## Cookbooks: + +* build-essential +* apt + +Opscode cookbooks (http://github.com/opscode/cookbooks/tree/master) + +ATTRIBUTES +========== + +* nodejs['install_method'] = source or package +* nodejs['version'] - release version of node to install +* nodejs['src_url'] - download location for node source tarball +* nodejs['dir'] - location where node will be installed, default /usr/local +* nodejs['npm'] - version of npm to install +* nodejs['npm_src_url'] - download location for npm source tarball +* nodejs['check_sha'] - test for valid sha_sum, default: true + +USAGE +===== + +Include the nodejs recipe to install node on your system based on the default installation method: + +* include_recipe "nodejs" + +Include the install_from_source recipe to install node from sources: + +* include_recipe "nodejs::install_from_source" + +Include the install_from_package recipe to install node from packages: +Note that only apt (Ubuntu, Debian) appears to have up to date packages available. +Centos, RHEL, etc are non-functional. (Try install_from_binary for those) + +* include_recipe "nodejs::install_from_package" + +Include the install_from_binary recipe to install node from official prebuilt binaries: +(Currently Linux x86, x86_64, armv6l only) + +* include_recipe "nodejs::install_from_binary" + +Include the npm recipe to install npm: + +* include_recipe "nodejs::npm" + +LICENSE and AUTHOR +================== + +Author:: Marius Ducea (marius@promethost.com) +Author:: Nathan L Smith (nlloyds@gmail.com) + +Copyright:: 2010-2012, Promet Solutions +Copyright:: 2012, Cramer Development, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/nodejs/Rakefile b/chef/cookbooks/nodejs/Rakefile new file mode 100644 index 0000000..f42f584 --- /dev/null +++ b/chef/cookbooks/nodejs/Rakefile @@ -0,0 +1,36 @@ +#!/usr/bin/env rake + +@cookbook = "nodejs" + +desc "Runs foodcritic linter" +task :foodcritic do + if Gem::Version.new("1.9.2") <= Gem::Version.new(RUBY_VERSION.dup) + sandbox = File.join(File.dirname(__FILE__), %w{tmp foodcritic}, @cookbook) + prepare_foodcritic_sandbox(sandbox) + + sh "foodcritic --epic-fail any #{File.dirname(sandbox)}" + else + puts "WARN: foodcritic run is skipped as Ruby #{RUBY_VERSION} is < 1.9.2." + end +end + +task :default => 'foodcritic' + +private + +def prepare_foodcritic_sandbox(sandbox) + files = %w{*.md *.rb attributes definitions files providers +recipes resources templates} + + rm_rf sandbox + mkdir_p sandbox + cp_r Dir.glob("{#{files.join(',')}}"), sandbox + puts "\n\n" +end + +begin + require 'kitchen/rake_tasks' + Kitchen::RakeTasks.new +rescue LoadError + puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI'] +end diff --git a/chef/cookbooks/nodejs/attributes/default.rb b/chef/cookbooks/nodejs/attributes/default.rb new file mode 100644 index 0000000..6863f81 --- /dev/null +++ b/chef/cookbooks/nodejs/attributes/default.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: nodejs +# Attributes:: nodejs +# +# Copyright 2010-2012, Promet Solutions +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] + when "smartos" + default['nodejs']['install_method'] = 'package' + else + default['nodejs']['install_method'] = 'source' +end + +default['nodejs']['version'] = '0.10.15' +default['nodejs']['checksum'] = '87345ab3b96aa02c5250d7b5ae1d80e620e8ae2a7f509f7fa18c4aaa340953e8' +default['nodejs']['checksum_linux_x64'] = '0b5191748a91b1c49947fef6b143f3e5e5633c9381a31aaa467e7c80efafb6e9' +default['nodejs']['checksum_linux_x86'] = '7ff9fb6aa19a5269a5a2f7a770040b8cd3c3b528a9c7c07da5da31c0d6dfde4d' +default['nodejs']['dir'] = '/usr/local' +default['nodejs']['npm'] = '1.3.5' +default['nodejs']['src_url'] = "http://nodejs.org/dist" +default['nodejs']['make_threads'] = node['cpu'] ? node['cpu']['total'].to_i : 2 +default['nodejs']['check_sha'] = true + +# Set this to true to install the legacy packages (0.8.x) from ubuntu/debian repositories; default is false (using the latest stable 0.10.x) +default['nodejs']['legacy_packages'] = false diff --git a/chef/cookbooks/nodejs/metadata.rb b/chef/cookbooks/nodejs/metadata.rb new file mode 100644 index 0000000..a4946d7 --- /dev/null +++ b/chef/cookbooks/nodejs/metadata.rb @@ -0,0 +1,22 @@ +maintainer "Promet Solutions" +maintainer_email "marius@promethost.com" +license "Apache 2.0" +description "Installs/Configures nodejs" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.3.0" +name "nodejs" +provides "nodejs" + +recipe "nodejs", "Installs Node.JS based on the default installation method" +recipe "nodejs::install_from_source", "Installs Node.JS from source" +recipe "nodejs::install_from_binary", "Installs Node.JS from official binaries" +recipe "nodejs::install_from_package", "Installs Node.JS from packages" +recipe "nodejs::npm", "Installs npm from source - a package manager for node" + +%w{ apt yum build-essential }.each do |c| + depends c +end + +%w{ debian ubuntu centos redhat smartos }.each do |os| + supports os +end diff --git a/chef/cookbooks/nodejs/recipes/default.rb b/chef/cookbooks/nodejs/recipes/default.rb new file mode 100644 index 0000000..e469e72 --- /dev/null +++ b/chef/cookbooks/nodejs/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Author:: Marius Ducea (marius@promethost.com) +# Cookbook Name:: nodejs +# Recipe:: default +# +# Copyright 2010-2012, Promet Solutions +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +case node['platform_family'] + when "debian" + include_recipe "apt" +end + +include_recipe "nodejs::install_from_#{node['nodejs']['install_method']}" diff --git a/chef/cookbooks/nodejs/recipes/install_from_binary.rb b/chef/cookbooks/nodejs/recipes/install_from_binary.rb new file mode 100644 index 0000000..768af64 --- /dev/null +++ b/chef/cookbooks/nodejs/recipes/install_from_binary.rb @@ -0,0 +1,80 @@ +# +# Author:: Julian Wilde (jules@jules.com.au) +# Cookbook Name:: nodejs +# Recipe:: install_from_binary +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +# Shamelessly borrowed from http://docs.opscode.com/dsl_recipe_method_platform.html +# Surely there's a more canonical way to get arch? +if node['kernel']['machine'] =~ /armv6l/ + arch = "arm-pi" # assume a raspberry pi +else + arch = node['kernel']['machine'] =~ /x86_64/ ? "x64" : "x86" +end + +distro_suffix = "-linux-#{arch}" + +# package_stub is for example: "node-v0.8.20-linux-x64" +package_stub = "node-v#{node['nodejs']['version']}#{distro_suffix}" +nodejs_tar = "#{package_stub}.tar.gz" +expected_checksum = node['nodejs']["checksum_linux_#{arch}"] + +nodejs_tar_path = nodejs_tar +if node['nodejs']['version'].split('.')[1].to_i >= 5 + nodejs_tar_path = "v#{node['nodejs']['version']}/#{nodejs_tar_path}" +end + +# Let the user override the source url in the attributes +nodejs_bin_url = "#{node['nodejs']['src_url']}/#{nodejs_tar_path}" + +# Download it: +remote_file "/usr/local/src/#{nodejs_tar}" do + source nodejs_bin_url + checksum expected_checksum + mode 0644 + action :create_if_missing +end + +# Where we will install the binaries and libs to (normally /usr/local): +destination_dir = node['nodejs']['dir'] + +install_not_needed = File.exists?("#{node['nodejs']['dir']}/bin/node") && `#{node['nodejs']['dir']}/bin/node --version`.chomp == "v#{node['nodejs']['version']}" + +# Verify the SHA sum of the downloaded file: +ruby_block "verify_sha_sum" do + block do + require 'digest/sha1' + calculated_sha256_hash = Digest::SHA256.file("/usr/local/src/#{nodejs_tar}") + if calculated_sha256_hash != expected_checksum + raise "SHA256 Hash of #{nodejs_tar} did not match! Expected #{expected_checksum} found #{calculated_sha256_hash}" + end + end + not_if { !node['nodejs']['check_sha'] or install_not_needed } +end + +# One hopes that we can trust the contents of the node tarball not to overwrite anything it shouldn't! +execute "install package to system" do + command <<-EOF + tar xf /usr/local/src/#{nodejs_tar} \ + --strip-components=1 --no-same-owner \ + -C #{destination_dir} \ + #{package_stub}/bin \ + #{package_stub}/lib \ + #{package_stub}/share + EOF + + not_if { install_not_needed } +end diff --git a/chef/cookbooks/nodejs/recipes/install_from_package.rb b/chef/cookbooks/nodejs/recipes/install_from_package.rb new file mode 100644 index 0000000..2736bfb --- /dev/null +++ b/chef/cookbooks/nodejs/recipes/install_from_package.rb @@ -0,0 +1,52 @@ +# +# Author:: Nathan L Smith (nlloyds@gmail.com) +# Author:: Marius Ducea (marius@promethost.com) +# Cookbook Name:: nodejs +# Recipe:: package +# +# Copyright 2012, Cramer Development, Inc. +# Copyright 2013, Opscale +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform_family'] + when 'debian' + if node['nodejs']['legacy_packages'] == true + repo = 'http://ppa.launchpad.net/chris-lea/node.js-legacy/ubuntu' + packages = %w{ nodejs npm } + else + repo = 'http://ppa.launchpad.net/chris-lea/node.js/ubuntu' + packages = %w{ nodejs } + end + apt_repository 'node.js' do + uri repo + distribution node['lsb']['codename'] + components ['main'] + keyserver "keyserver.ubuntu.com" + key "C7917B12" + action :add + end + when 'rhel' + include_recipe 'yum::epel' + packages = %w{ nodejs nodejs-devel npm } + when 'smartos' + packages = %w{ nodejs } + else + Chef::Log.error "There are no nodejs packages for this platform; please use the source or binary method to install node" + return +end + +packages.each do |node_pkg| + package node_pkg +end diff --git a/chef/cookbooks/nodejs/recipes/install_from_source.rb b/chef/cookbooks/nodejs/recipes/install_from_source.rb new file mode 100644 index 0000000..710604a --- /dev/null +++ b/chef/cookbooks/nodejs/recipes/install_from_source.rb @@ -0,0 +1,68 @@ +# +# Author:: Marius Ducea (marius@promethost.com) +# Cookbook Name:: nodejs +# Recipe:: source +# +# Copyright 2010-2012, Promet Solutions +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "build-essential" + +case node['platform_family'] + when 'rhel','fedora' + package "openssl-devel" + when 'debian' + package "libssl-dev" +end + +nodejs_tar = "node-v#{node['nodejs']['version']}.tar.gz" +nodejs_tar_path = nodejs_tar +if node['nodejs']['version'].split('.')[1].to_i >= 5 + nodejs_tar_path = "v#{node['nodejs']['version']}/#{nodejs_tar_path}" +end +# Let the user override the source url in the attributes +nodejs_src_url = "#{node['nodejs']['src_url']}/#{nodejs_tar_path}" + +remote_file "/usr/local/src/#{nodejs_tar}" do + source nodejs_src_url + checksum node['nodejs']['checksum'] + mode 0644 + action :create_if_missing +end + +# --no-same-owner required overcome "Cannot change ownership" bug +# on NFS-mounted filesystem +execute "tar --no-same-owner -zxf #{nodejs_tar}" do + cwd "/usr/local/src" + creates "/usr/local/src/node-v#{node['nodejs']['version']}" +end + +bash "compile node.js (on #{node['nodejs']['make_threads']} cpu)" do + # OSX doesn't have the attribute so arbitrarily default 2 + cwd "/usr/local/src/node-v#{node['nodejs']['version']}" + code <<-EOH + PATH="/usr/local/bin:$PATH" + ./configure --prefix=#{node['nodejs']['dir']} && \ + make -j #{node['nodejs']['make_threads']} + EOH + creates "/usr/local/src/node-v#{node['nodejs']['version']}/node" +end + +execute "nodejs make install" do + environment({"PATH" => "/usr/local/bin:/usr/bin:/bin:$PATH"}) + command "make install" + cwd "/usr/local/src/node-v#{node['nodejs']['version']}" + not_if {::File.exists?("#{node['nodejs']['dir']}/bin/node") && `#{node['nodejs']['dir']}/bin/node --version`.chomp == "v#{node['nodejs']['version']}" } +end diff --git a/chef/cookbooks/nodejs/recipes/npm.rb b/chef/cookbooks/nodejs/recipes/npm.rb new file mode 100644 index 0000000..a16f27a --- /dev/null +++ b/chef/cookbooks/nodejs/recipes/npm.rb @@ -0,0 +1,38 @@ +# +# Author:: Marius Ducea (marius@promethost.com) +# Cookbook Name:: nodejs +# Recipe:: npm +# +# Copyright 2010-2012, Promet Solutions +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "nodejs" + +package "curl" + +npm_src_url = "http://registry.npmjs.org/npm/-/npm-#{node['nodejs']['npm']}.tgz" + +bash "install npm - package manager for node" do + cwd "/usr/local/src" + user "root" + code <<-EOH + mkdir -p npm-v#{node['nodejs']['npm']} && \ + cd npm-v#{node['nodejs']['npm']} + curl -L #{npm_src_url} | tar xzf - --strip-components=1 && \ + make uninstall dev + EOH + not_if "#{node['nodejs']['dir']}/bin/npm -v 2>&1 | grep '#{node['nodejs']['npm']}'" +end + diff --git a/chef/cookbooks/nodejs/test/integration/package/bats/package.bats b/chef/cookbooks/nodejs/test/integration/package/bats/package.bats new file mode 100644 index 0000000..508b9ca --- /dev/null +++ b/chef/cookbooks/nodejs/test/integration/package/bats/package.bats @@ -0,0 +1,9 @@ +#!/usr/bin/env bats + +@test "node should be in the path" { + [ "$(command -v node)" ] +} + +@test "npm should be in the path" { + [ "$(command -v npm)" ] +} diff --git a/chef/cookbooks/nodejs/test/integration/source/bats/source.bats b/chef/cookbooks/nodejs/test/integration/source/bats/source.bats new file mode 100644 index 0000000..508b9ca --- /dev/null +++ b/chef/cookbooks/nodejs/test/integration/source/bats/source.bats @@ -0,0 +1,9 @@ +#!/usr/bin/env bats + +@test "node should be in the path" { + [ "$(command -v node)" ] +} + +@test "npm should be in the path" { + [ "$(command -v npm)" ] +} diff --git a/chef/cookbooks/nodejs/test/support/Gemfile b/chef/cookbooks/nodejs/test/support/Gemfile new file mode 100644 index 0000000..d45a670 --- /dev/null +++ b/chef/cookbooks/nodejs/test/support/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem 'rake' +gem 'foodcritic' + diff --git a/chef/cookbooks/ntp/.kitchen.yml b/chef/cookbooks/ntp/.kitchen.yml new file mode 100644 index 0000000..0fa22f7 --- /dev/null +++ b/chef/cookbooks/ntp/.kitchen.yml @@ -0,0 +1,32 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + +suites: +- name: default + run_list: + - recipe[minitest-handler] + - recipe[ntp] + - recipe[ntp::ntpdate] + attributes: {} diff --git a/chef/cookbooks/ntp/.tailor b/chef/cookbooks/ntp/.tailor new file mode 100644 index 0000000..efc4b24 --- /dev/null +++ b/chef/cookbooks/ntp/.tailor @@ -0,0 +1,106 @@ +#------------------------------------------------------------------------------ +# Horizontal Whitespace +#------------------------------------------------------------------------------ +# allow_hard_tabs True to let hard tabs be considered a single space. +# Default: false +# +# allow_trailing_line_spaces +# True to skip detecting extra spaces at the ends of +# lines. +# Default: false +# +# indentation_spaces The number of spaces to consider a proper indent. +# Default: 2 +# +# max_line_length The maximum number of characters in a line before +# tailor complains. +# Default: 80 +# spaces_after_comma Number of spaces to expect after a comma. +# Default: 1 +# +# spaces_before_comma Number of spaces to expect before a comma. +# Default: 0 +# +# spaces_after_lbrace The number of spaces to expect after an lbrace ('{'). +# Default: 1 +# +# spaces_before_lbrace The number of spaces to expect before an lbrace ('{'). +# Default: 1 +# +# spaces_before_rbrace The number of spaces to expect before an rbrace ('}'). +# Default: 1 +# +# spaces_in_empty_braces The number of spaces to expect between braces when +# there's nothing in the braces (i.e. {}). +# Default: 0 +# +# spaces_after_lbracket The number of spaces to expect after an +# lbracket ('['). +# Default: 0 +# +# spaces_before_rbracket The number of spaces to expect before an +# rbracket (']'). +# Default: 0 +# +# spaces_after_lparen The number of spaces to expect after an +# lparen ('('). +# Default: 0 +# +# spaces_before_rparen The number of spaces to expect before an +# rbracket (')'). +# Default: 0 +# +#------------------------------------------------------------------------------ +# Naming +#------------------------------------------------------------------------------ +# allow_camel_case_methods +# Setting to true skips detection of camel-case method +# names (i.e. def myMethod). +# Default: false +# +# allow_screaming_snake_case_classes +# Setting to true skips detection of screaming +# snake-case class names (i.e. My_Class). +# Default: false +# +#------------------------------------------------------------------------------ +# Vertical Whitespace +#------------------------------------------------------------------------------ +# max_code_lines_in_class The number of lines of code in a class to allow before +# tailor will warn you. +# Default: 300 +# +# max_code_lines_in_method +# The number of lines of code in a method to allow +# before tailor will warn you. +# Default: 30 +# +# trailing_newlines The number of newlines that should be at the end of +# the file. +# Default: 1 +# +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.max_line_length 80, level: :off + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/ntp/.travis.yml b/chef/cookbooks/ntp/.travis.yml new file mode 100644 index 0000000..1fc926e --- /dev/null +++ b/chef/cookbooks/ntp/.travis.yml @@ -0,0 +1,5 @@ +language: ruby +bundler_args: --without development +rvm: + - 1.9.3 +script: bundle exec rake tailor test foodcritic diff --git a/chef/cookbooks/ntp/Berksfile b/chef/cookbooks/ntp/Berksfile new file mode 100644 index 0000000..3640515 --- /dev/null +++ b/chef/cookbooks/ntp/Berksfile @@ -0,0 +1,9 @@ +site :opscode + +group :integration do + cookbook "apt", '~> 2.0' + cookbook "yum", '~> 2.3' + cookbook "minitest-handler", '~> 0.2' +end + +metadata diff --git a/chef/cookbooks/ntp/CHANGELOG.md b/chef/cookbooks/ntp/CHANGELOG.md new file mode 100644 index 0000000..7d75b3f --- /dev/null +++ b/chef/cookbooks/ntp/CHANGELOG.md @@ -0,0 +1,49 @@ +ntp Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the ntp cookbook. + + +v1.4.0 +------ +### Improvement +- **[COOK-3365](https://tickets.opscode.com/browse/COOK-3365)** - Update ntp leapseconds file to version 3597177600 +- **[COOK-1674](https://tickets.opscode.com/browse/COOK-1674)** - Add Windows support + +v1.3.2 +------ +- [COOK-2024] - update leapfile for IERS Bulletin C + +v1.3.0 +------ +- [COOK-1404] - add leapfile for handling leap seconds + +v1.2.0 +------ +- [COOK-1184] - Add recipe to disable NTP completely +- [COOK-1298] - Refactor into a reference cookbook for testing + +v1.1.8 +------ +- [COOK-1158] - RHEL family >= 6 has ntpdate package + +v1.1.6 +------ +- Related to changes in COOK-1124, fix group for freebsd and else + +v1.1.4 +------ +- [COOK-1124] - parameterised driftfile and statsdir to be configurable by platform + +v1.1.2 +------ +- [COOK-952] - freebsd support +- [COOK-949] - check for any virtual system not just vmware + +v1.1.0 +------ +- Fixes COOK-376 (use LAN peers, iburst option, LAN restriction attribute) + +v1.0.1 +------ +- Support scientific linux +- Use service name attribute in resource (fixes EL derivatives) diff --git a/chef/cookbooks/ntp/CONTRIBUTING b/chef/cookbooks/ntp/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/ntp/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/ntp/Gemfile b/chef/cookbooks/ntp/Gemfile new file mode 100644 index 0000000..d940a9d --- /dev/null +++ b/chef/cookbooks/ntp/Gemfile @@ -0,0 +1,24 @@ +source "https://rubygems.org" + + gem "berkshelf", "~> 2.0" + gem "thor-foodcritic", "~> 0.2" + +group :test do + gem "chefspec", "~> 2.0" + gem "foodcritic", "~> 2.0" + gem "rake", "~> 10.1" + gem "tailor", "~> 1.2" +end + +group :development do + gem "guard", "~> 1.8" + gem "guard-kitchen", "~> 0.0" + gem "guard-rspec", "~> 3.0" + gem "rb-fchange", "~> 0.0" + gem "rb-fsevent", "~> 0.9" + gem "rb-inotify", "~> 0.9" + gem "ruby_gntp", "~> 0.3" +end + +gem "test-kitchen", "~> 1.0.0.beta.3",:group => :development +gem "kitchen-vagrant", "~> 0.11", :group => :development diff --git a/chef/cookbooks/ntp/Guardfile b/chef/cookbooks/ntp/Guardfile new file mode 100644 index 0000000..0e8fce5 --- /dev/null +++ b/chef/cookbooks/ntp/Guardfile @@ -0,0 +1,25 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +guard 'rspec', :cli => "--color" do + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch('spec/spec_helper.rb') { "spec" } + + watch(%r{^recipes/(.+)\.rb$}) { |m| "spec/unit/recipes/#{m[1]}_spec.rb" } + watch(%r{^attributes/(.+)\.rb$}) + watch(%r{^files/(.+)}) + watch(%r{^templates/(.+)}) + watch(%r{^providers/(.+)\.rb}) { |m| "spec/unit/providers/#{m[1]}_spec.rb" } + watch(%r{^resources/(.+)\.rb}) { |m| "spec/unit/resources/#{m[1]}_spec.rb" } +end + +guard 'kitchen' do + watch(%r{test/.+}) + watch(%r{^recipes/(.+)\.rb$}) + watch(%r{^attributes/(.+)\.rb$}) + watch(%r{^files/(.+)}) + watch(%r{^templates/(.+)}) + watch(%r{^providers/(.+)\.rb}) + watch(%r{^resources/(.+)\.rb}) +end diff --git a/chef/cookbooks/ntp/LICENSE b/chef/cookbooks/ntp/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/ntp/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/ntp/README.md b/chef/cookbooks/ntp/README.md new file mode 100644 index 0000000..61201a5 --- /dev/null +++ b/chef/cookbooks/ntp/README.md @@ -0,0 +1,207 @@ +NTP Cookbook +============ +# NTP [![Build Status](https://secure.travis-ci.org/opscode-cookbooks/ntp.png?branch=master)](http://travis-ci.org/opscode-cookbooks/ntp) + +Installs and configures ntp, optionally configure ntpdate on debian family platforms. On Windows systems it uses the Meinberg port of the standard NTPd client to Windows. + +### About the refactor + +This recipe was heavily re-factored as a Hackday exercise at Chefconf 2012. +The purpose of refactoring was to have a simple community cookbook which +serves as a testing documentation reference. We chose a lightweight testing method +using minitest to validate the sanity of our default attributes. + +More information on our testing strategy used in this cookbook is available +in the TESTING. Along with information on how to use this type of lightweight +testing in your own cookbooks. + +#### IMPORTANT NOTES + +Breaking changes are the absence of an ntp::disable recipe. This was factored +out into an ntp::undo corresponding to the default recipe and a separate +ntp::ntpdate recipe. + +The ntp::undo recipe stops and removes ntp components. The ntp::ntpdate +recipe configures the ntpdate component. The ntp['ntpdate']['disable'] boolean +will disable the ntpdate-debian command on Debian family distributions. + + +Requirements +------------ +### Operating Systems +- Debian-family Linux Distributions +- RedHat-family Linux Distributions +- FreeBSD +- Windows + +### Cookbooks +- When running on Windows based systems the node must include the Windows cookbook. This + cookbook suggests the Windows cookbook in the metadata so as to not force including the + Windows cookbook on *nix systems. You can change the suggests to depends if you require + Windows platform support + + +Attributes +---------- +### Recommended tunables + +* ntp['servers'] (applies to NTP Servers and Clients) + + - Array, should be a list of upstream NTP public servers. The NTP protocol + works best with at least 3 servers. The NTPD maximum is 7 upstream + servers, any more than that and some of them will be ignored by the daemon. + +* ntp['peers'] (applies to NTP Servers ONLY) + + - Array, should be a list of local NTP private servers. Configuring peer + servers on your LAN will reduce traffic to upstream time sources, and + provide higher availability of NTP on your LAN. Again the maximum is 7 + peers + +* ntp['restrictions'] (applies to NTP Servers only) + + - Array, should be a list of restrict lines to restrict access to NTP + clients on your LAN. + +* ntp['ntpdate']['disable'] + + - Boolean, disables the use of ntpdate-debian if set to true. + - Defaults to false, and will not disable ntpdate. There is usually no + init service to manage with ntpdate. Therefore it should not conflict + with ntpd in most cases. + +### Platform specific + +* ntp['packages'] + + - Array, the packages to install + - Default, ntp for everything, ntpdate depending on platform. Not applicable for + Windows nodes + +* ntp['service'] + + - String, the service to act on + - Default, ntp, NTP, or ntpd, depending on platform + +* ntp['conffile'] + + - String, the path to the ntp configuration file. + - Default, platform-specific location. + +* ntp['driftfile'] + + - String, the path to the frequency file. + - Default, platform-specific location. + +* ntp['varlibdir'] + + - String, the path to /var/lib files such as the driftfile. + - Default, platform-specific location. Not applicable for Windows nodes + +* ntp['statsdir'] + + - String, the directory path for files created by the statistics facility. + - Default, platform-specific location. Not applicable for Windows nodes + +* ntp['conf\_owner'] and ntp['conf\_group'] + + - String, the owner and group of the sysconf directory files, such as /etc/ntp.conf. + - Default, platform-specific root:root or root:wheel. + +* ntp['var\_owner'] and ntp['var\_group'] + + - String, the owner and group of the /var/lib directory files, such as /var/lib/ntp. + - Default, platform-specific ntp:ntp or root:wheel. Not applicable for Windows nodes + +* ntp['package_url'] + + - String, the URL to the the Meinberg NTPd client installation package. + - Default, Meinberg site download URL + - Windows platform only + +* ntp['vs_runtime_url'] + + - String, the URL to the the Visual Studio C++ 2008 runtime libraries that are required + for the Meinberg NTP client. + - Default, Microsoft site download URL + - Windows platform only + +* ntp['vs_runtime_productname'] + + - String, the installation name of the Visual Studio C++ Runtimes file. + - Default, "Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022" + - Windows platform only + + +Usage +----- +### default recipe + +Set up the ntp attributes in a role. For example in a base.rb role applied to all nodes: + + name "base" + description "Role applied to all systems" + default_attributes( + "ntp" => { + "servers" => ["time0.int.example.org", "time1.int.example.org"] + } + ) + +Then in an ntpserver.rb role that is applied to NTP servers (e.g., time.int.example.org): + + name "ntp_server" + description "Role applied to the system that should be an NTP server." + default_attributes( + "ntp" => { + "is_server" => "true", + "servers" => ["0.pool.ntp.org", "1.pool.ntp.org"], + "peers" => ["time0.int.example.org", "time1.int.example.org"], + "restrictions" => ["10.0.0.0 mask 255.0.0.0 nomodify notrap"] + } + ) + +The timeX.int.example.org used in these roles should be the names or IP addresses of internal NTP servers. +Then simply add ntp, or ntp::default to your run\_list to apply the ntp daemon's configuration. + +### ntpdate recipe + +On Debian-family platforms, and newer versions of RedHat, there is a separate ntpdate package. + +You may blank out the ntpdate configuration file by overriding ntp['ntpdate']['disable'] to `true`. +Then include the ntp::ntpdate recipe in your run\_list. + +You may re-enable the ntpdate configuration by ensuring ntp['ntpdate']['disable'] is `false`. +Then include the ntp::ntpdate recipe in your run\_list. + +### undo recipe + +If for some reason you need to stop and remove the ntp daemon, you can apply this recipe by adding +ntp::undo to your run\_list. The undo recipe is not supported on Windows at the moment. + + +License & Authors +----------------- +- Author:: Joshua Timberman () +- Contributor:: Eric G. Wolfe () +- Contributor:: Fletcher Nichol () +- Contributor:: Tim Smith () + +```text +Copyright 2009-2011, Opscode, Inc. +Copyright 2012, Eric G. Wolfe +Copyright 2012, Fletcher Nichol +Copyright 2012, Webtrends, Inc. +Copyright 2013, Limelight Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/ntp/Rakefile b/chef/cookbooks/ntp/Rakefile new file mode 100644 index 0000000..69c3871 --- /dev/null +++ b/chef/cookbooks/ntp/Rakefile @@ -0,0 +1,27 @@ +#!/usr/bin/env rake +require 'tailor/rake_task' +require 'rspec/core/rake_task' +require 'foodcritic' + +RSpec::Core::RakeTask.new(:spec) +desc "Runs rspec tests" +task :test => :spec + +desc "Runs foodcritic linter" +task :foodcritic do + if Gem::Version.new("1.9.2") <= Gem::Version.new(RUBY_VERSION.dup) + FoodCritic::Rake::LintTask.new do |t| + t.options = {:fail_tags => ['any']} + end + else + puts "WARN: foodcritic run is skipped as Ruby #{RUBY_VERSION} is < 1.9.2." + end +end + +desc "Runs tailor against the cookbook." +task :tailor do + Tailor::RakeTask.new +end + +# Tailor before rspec so we don't tailor vendored cookbooks +task :default => ['tailor', 'test', 'foodcritic'] diff --git a/chef/cookbooks/ntp/TESTING.md b/chef/cookbooks/ntp/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/ntp/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/ntp/attributes/default.rb b/chef/cookbooks/ntp/attributes/default.rb new file mode 100644 index 0000000..e4f1db4 --- /dev/null +++ b/chef/cookbooks/ntp/attributes/default.rb @@ -0,0 +1,65 @@ +# +# Cookbook Name:: ntp +# Attributes:: default +# +# Author:: Joshua Timberman () +# Author:: Tim Smith () +# +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# default attributes for all platforms +default['ntp']['servers'] = Array.new +default['ntp']['peers'] = Array.new +default['ntp']['restrictions'] = Array.new + +default['ntp']['packages'] = %w{ ntp ntpdate } +default['ntp']['service'] = "ntp" +default['ntp']['varlibdir'] = "/var/lib/ntp" +default['ntp']['driftfile'] = "#{node['ntp']['varlibdir']}/ntp.drift" +default['ntp']['conffile'] = "/etc/ntp.conf" +default['ntp']['statsdir'] = "/var/log/ntpstats/" +default['ntp']['conf_owner'] = "root" +default['ntp']['conf_group'] = "root" +default['ntp']['var_owner'] = "ntp" +default['ntp']['var_group'] = "ntp" +default['ntp']['leapfile'] = "/etc/ntp.leapseconds" + +# overrides on a platform-by-platform basis +case platform +when "redhat", "centos", "fedora", "scientific", "amazon", "oracle" + default['ntp']['service'] = "ntpd" + default['ntp']['packages'] = %w{ ntp } + if node['platform_version'].to_i >= 6 + default['ntp']['packages'] = %w{ ntp ntpdate } + end +when "freebsd" + default['ntp']['service'] = "ntpd" + default['ntp']['varlibdir'] = "/var/db" + default['ntp']['driftfile'] = "#{node['ntp']['varlibdir']}/ntpd.drift" + default['ntp']['statsdir'] = "#{node['ntp']['varlibdir']}/ntpstats" + default['ntp']['packages'] = %w{ ntp } + default['ntp']['conf_group'] = "wheel" + default['ntp']['var_group'] = "wheel" +when "windows" + default['ntp']['service'] = "NTP" + default['ntp']['driftfile'] = "C:\\NTP\\ntp.drift" + default['ntp']['conffile'] = "C:\\NTP\\etc\\ntp.conf" + default['ntp']['conf_owner'] = "Administrators" + default['ntp']['conf_group'] = "Administrators" + default['ntp']['package_url'] = "http://www.meinbergglobal.com/download/ntp/windows/ntp-4.2.6p5@london-o-lpv-win32-setup.exe" + default['ntp']['vs_runtime_url'] = "http://download.microsoft.com/download/1/1/1/1116b75a-9ec3-481a-a3c8-1777b5381140/vcredist_x86.exe" + default['ntp']['vs_runtime_productname'] = "Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022" +end diff --git a/chef/cookbooks/ntp/attributes/ntpdate.rb b/chef/cookbooks/ntp/attributes/ntpdate.rb new file mode 100644 index 0000000..88bc425 --- /dev/null +++ b/chef/cookbooks/ntp/attributes/ntpdate.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: ntp +# Attributes:: default +# +# Author:: Joshua Timberman () +# +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +default['ntp']['ntpdate']['disable'] = false diff --git a/chef/cookbooks/ntp/chefignore b/chef/cookbooks/ntp/chefignore new file mode 100644 index 0000000..b616c03 --- /dev/null +++ b/chef/cookbooks/ntp/chefignore @@ -0,0 +1,19 @@ +# Put files/directories that should be ignored in this file. +# Lines that start with '# ' are comments. + +# gitignore +\.gitignore + +# emacs +*~ + +# vim +*.sw[a-z] + +# subversion +*/.svn/* + +# tests +*/test/* +\.travis.yml +Rakefile diff --git a/chef/cookbooks/ntp/files/default/ntp.ini b/chef/cookbooks/ntp/files/default/ntp.ini new file mode 100644 index 0000000..11498b4 --- /dev/null +++ b/chef/cookbooks/ntp/files/default/ntp.ini @@ -0,0 +1,23 @@ +[Installer] +InstallDir=C:\NTP +UpgradeMode=Reinstall +Logfile=C:\NTP\install.log +Silent=yes + +[Components] +InstallDocs=yes +InstallTools=yes +InstallOpenSSL=yes +CreateStartMenuEntries=yes + +[Service] +ModifyFirewall=yes +ServiceAccount=@SYSTEM +DisableOthers=yes +AllowBigInitialTimestep=yes +EnableMMTimer=yes +AutoStart=yes +StartAfterInstallation=yes + +[Configuration] +UseConfigFile=C:\NTP\ntp.conf diff --git a/chef/cookbooks/ntp/files/default/ntp.leapseconds b/chef/cookbooks/ntp/files/default/ntp.leapseconds new file mode 100644 index 0000000..4733673 --- /dev/null +++ b/chef/cookbooks/ntp/files/default/ntp.leapseconds @@ -0,0 +1,231 @@ +# +# In the following text, the symbol '#' introduces +# a comment, which continues from that symbol until +# the end of the line. A plain comment line has a +# whitespace character following the comment indicator. +# There are also special comment lines defined below. +# A special comment will always have a non-whitespace +# character in column 2. +# +# A blank line should be ignored. +# +# The following table shows the corrections that must +# be applied to compute International Atomic Time (TAI) +# from the Coordinated Universal Time (UTC) values that +# are transmitted by almost all time services. +# +# The first column shows an epoch as a number of seconds +# since 1900.0 and the second column shows the number of +# seconds that must be added to UTC to compute TAI for +# any timestamp at or after that epoch. The value on +# each line is valid from the indicated initial instant +# until the epoch given on the next one or indefinitely +# into the future if there is no next line. +# (The comment on each line shows the representation of +# the corresponding initial epoch in the usual +# day-month-year format. The epoch always begins at +# 00:00:00 UTC on the indicated day. See Note 5 below.) +# +# Important notes: +# +# 1. Coordinated Universal Time (UTC) is often referred to +# as Greenwich Mean Time (GMT). The GMT time scale is no +# longer used, and the use of GMT to designate UTC is +# discouraged. +# +# 2. The UTC time scale is realized by many national +# laboratories and timing centers. Each laboratory +# identifies its realization with its name: Thus +# UTC(NIST), UTC(USNO), etc. The differences among +# these different realizations are typically on the +# order of a few nanoseconds (i.e., 0.000 000 00x s) +# and can be ignored for many purposes. These differences +# are tabulated in Circular T, which is published monthly +# by the International Bureau of Weights and Measures +# (BIPM). See www.bipm.fr for more information. +# +# 3. The current defintion of the relationship between UTC +# and TAI dates from 1 January 1972. A number of different +# time scales were in use before than epoch, and it can be +# quite difficult to compute precise timestamps and time +# intervals in those "prehistoric" days. For more information, +# consult: +# +# The Explanatory Supplement to the Astronomical +# Ephemeris. +# or +# Terry Quinn, "The BIPM and the Accurate Measurement +# of Time," Proc. of the IEEE, Vol. 79, pp. 894-905, +# July, 1991. +# +# 4. The insertion of leap seconds into UTC is currently the +# responsibility of the International Earth Rotation Service, +# which is located at the Paris Observatory: +# +# Central Bureau of IERS +# 61, Avenue de l'Observatoire +# 75014 Paris, France. +# +# Leap seconds are announced by the IERS in its Bulletin C +# +# See hpiers.obspm.fr or www.iers.org for more details. +# +# All national laboratories and timing centers use the +# data from the BIPM and the IERS to construct their +# local realizations of UTC. +# +# Although the definition also includes the possibility +# of dropping seconds ("negative" leap seconds), this has +# never been done and is unlikely to be necessary in the +# foreseeable future. +# +# 5. If your system keeps time as the number of seconds since +# some epoch (e.g., NTP timestamps), then the algorithm for +# assigning a UTC time stamp to an event that happens during a positive +# leap second is not well defined. The official name of that leap +# second is 23:59:60, but there is no way of representing that time +# in these systems. +# Many systems of this type effectively stop the system clock for +# one second during the leap second and use a time that is equivalent +# to 23:59:59 UTC twice. For these systems, the corresponding TAI +# timestamp would be obtained by advancing to the next entry in the +# following table when the time equivalent to 23:59:59 UTC +# is used for the second time. Thus the leap second which +# occurred on 30 June 1972 at 23:59:59 UTC would have TAI +# timestamps computed as follows: +# +# ... +# 30 June 1972 23:59:59 (2287785599, first time): TAI= UTC + 10 seconds +# 30 June 1972 23:59:60 (2287785599,second time): TAI= UTC + 11 seconds +# 1 July 1972 00:00:00 (2287785600) TAI= UTC + 11 seconds +# ... +# +# If your system realizes the leap second by repeating 00:00:00 UTC twice +# (this is possible but not usual), then the advance to the next entry +# in the table must occur the second time that a time equivlent to +# 00:00:00 UTC is used. Thus, using the same example as above: +# +# ... +# 30 June 1972 23:59:59 (2287785599): TAI= UTC + 10 seconds +# 30 June 1972 23:59:60 (2287785600, first time): TAI= UTC + 10 seconds +# 1 July 1972 00:00:00 (2287785600,second time): TAI= UTC + 11 seconds +# ... +# +# in both cases the use of timestamps based on TAI produces a smooth +# time scale with no discontinuity in the time interval. +# +# This complexity would not be needed for negative leap seconds (if they +# are ever used). The UTC time would skip 23:59:59 and advance from +# 23:59:58 to 00:00:00 in that case. The TAI offset would decrease by +# 1 second at the same instant. This is a much easier situation to deal +# with, since the difficulty of unambiguously representing the epoch +# during the leap second does not arise. +# +# Questions or comments to: +# Judah Levine +# Time and Frequency Division +# NIST +# Boulder, Colorado +# jlevine@boulder.nist.gov +# +# Last Update of leap second values: 11 January 2012 +# +# The following line shows this last update date in NTP timestamp +# format. This is the date on which the most recent change to +# the leap second data was added to the file. This line can +# be identified by the unique pair of characters in the first two +# columns as shown below. +# +#$ 3535228800 +# +# The NTP timestamps are in units of seconds since the NTP epoch, +# which is 1900.0. The Modified Julian Day number corresponding +# to the NTP time stamp, X, can be computed as +# +# X/86400 + 15020 +# +# where the first term converts seconds to days and the second +# term adds the MJD corresponding to 1900.0. The integer portion +# of the result is the integer MJD for that day, and any remainder +# is the time of day, expressed as the fraction of the day since 0 +# hours UTC. The conversion from day fraction to seconds or to +# hours, minutes, and seconds may involve rounding or truncation, +# depending on the method used in the computation. +# +# The data in this file will be updated periodically as new leap +# seconds are announced. In addition to being entered on the line +# above, the update time (in NTP format) will be added to the basic +# file name leap-seconds to form the name leap-seconds.. +# In addition, the generic name leap-seconds.list will always point to +# the most recent version of the file. +# +# This update procedure will be performed only when a new leap second +# is announced. +# +# The following entry specifies the expiration date of the data +# in this file in units of seconds since 1900.0. This expiration date +# will be changed at least twice per year whether or not a new leap +# second is announced. These semi-annual changes will be made no +# later than 1 June and 1 December of each year to indicate what +# action (if any) is to be taken on 30 June and 31 December, +# respectively. (These are the customary effective dates for new +# leap seconds.) This expiration date will be identified by a +# unique pair of characters in columns 1 and 2 as shown below. +# In the unlikely event that a leap second is announced with an +# effective date other than 30 June or 31 December, then this +# file will be edited to include that leap second as soon as it is +# announced or at least one month before the effective date +# (whichever is later). +# If an announcement by the IERS specifies that no leap second is +# scheduled, then only the expiration date of the file will +# be advanced to show that the information in the file is still +# current -- the update time stamp, the data and the name of the file +# will not change. +# +# Updated through IERS Bulletin C45 +# File expires on: 28 December 2013 +# +#@ 3597177600 +# +2272060800 10 # 1 Jan 1972 +2287785600 11 # 1 Jul 1972 +2303683200 12 # 1 Jan 1973 +2335219200 13 # 1 Jan 1974 +2366755200 14 # 1 Jan 1975 +2398291200 15 # 1 Jan 1976 +2429913600 16 # 1 Jan 1977 +2461449600 17 # 1 Jan 1978 +2492985600 18 # 1 Jan 1979 +2524521600 19 # 1 Jan 1980 +2571782400 20 # 1 Jul 1981 +2603318400 21 # 1 Jul 1982 +2634854400 22 # 1 Jul 1983 +2698012800 23 # 1 Jul 1985 +2776982400 24 # 1 Jan 1988 +2840140800 25 # 1 Jan 1990 +2871676800 26 # 1 Jan 1991 +2918937600 27 # 1 Jul 1992 +2950473600 28 # 1 Jul 1993 +2982009600 29 # 1 Jul 1994 +3029443200 30 # 1 Jan 1996 +3076704000 31 # 1 Jul 1997 +3124137600 32 # 1 Jan 1999 +3345062400 33 # 1 Jan 2006 +3439756800 34 # 1 Jan 2009 +3550089600 35 # 1 Jul 2012 +# +# the following special comment contains the +# hash value of the data in this file computed +# use the secure hash algorithm as specified +# by FIPS 180-1. See the files in ~/pub/sha for +# the details of how this hash value is +# computed. Note that the hash computation +# ignores comments and whitespace characters +# in data lines. It includes the NTP values +# of both the last modification time and the +# expiration time of the file, but not the +# white space on those lines. +# the hash line is also ignored in the +# computation. +# +#h abf85ecb f7dd8b73 964b20af 28692645 caa5fd81 diff --git a/chef/cookbooks/ntp/files/default/tests/minitest/default_test.rb b/chef/cookbooks/ntp/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..bfd13cf --- /dev/null +++ b/chef/cookbooks/ntp/files/default/tests/minitest/default_test.rb @@ -0,0 +1,24 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'ntp::default' do + + include Helpers::Ntp + + it 'Starts the NTP daemon' do + service(node['ntp']['service']).must_be_running + service(node['ntp']['service']).must_be_enabled + end + + it 'Creates the leapfile' do + file(node['ntp']['leapfile']).must_exist.with(:owner, node['ntp']['conf_owner']).and(:group, node['ntp']['conf_group']) + end + + it 'Creates the ntp.conf' do + file('/etc/ntp.conf').must_exist.with(:owner, node['ntp']['conf_owner']).and(:group, node['ntp']['conf_group']) + + node['ntp']['servers'].each do |s| + file('/etc/ntp.conf').must_include s + end + end + +end diff --git a/chef/cookbooks/ntp/files/default/tests/minitest/disable_test.rb b/chef/cookbooks/ntp/files/default/tests/minitest/disable_test.rb new file mode 100644 index 0000000..293f5aa --- /dev/null +++ b/chef/cookbooks/ntp/files/default/tests/minitest/disable_test.rb @@ -0,0 +1,26 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'ntp::disable' do + + include Helpers::Ntp + + it 'Disables the NTP daemon' do + service(node['ntp']['service']).wont_be_running + service(node['ntp']['service']).wont_be_enabled + end + + it 'Creates the ntpdate conf file' do + skip unless ["debian"].include? node['platform_family'] + + if node['ntp']['ntpdate']['disable'] + file('/etc/default/ntpdate').must_include "exit 0" + else + file('/etc/default/ntpdate').wont_include "exit 0" + end + + file('/etc/default/ntpdate').must_exist.with( + :owner, node['ntp']['conf_owner']).and( + :group, node['ntp']['conf_group']) + end + +end diff --git a/chef/cookbooks/ntp/files/default/tests/minitest/ntpdate_test.rb b/chef/cookbooks/ntp/files/default/tests/minitest/ntpdate_test.rb new file mode 100644 index 0000000..ae50219 --- /dev/null +++ b/chef/cookbooks/ntp/files/default/tests/minitest/ntpdate_test.rb @@ -0,0 +1,26 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'ntp::ntpdate' do + + include Helpers::Ntp + + it 'Installs the ntpdate package' do + skip unless ["debian"].include? node['platform_family'] + package("less").must_be_installed + end + + it 'Creates the ntpdate conf file' do + skip unless ["debian"].include? node['platform_family'] + + if node['ntp']['ntpdate']['disable'] + file('/etc/default/ntpdate').must_include "exit 0" + else + file('/etc/default/ntpdate').wont_include "exit 0" + end + + file('/etc/default/ntpdate').must_exist.with( + :owner, node['ntp']['conf_owner']).and( + :group, node['ntp']['conf_group']) + end + +end diff --git a/chef/cookbooks/ntp/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/ntp/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..87b1b0a --- /dev/null +++ b/chef/cookbooks/ntp/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,7 @@ +module Helpers + module Ntp + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + end +end diff --git a/chef/cookbooks/ntp/files/default/tests/minitest/undo_test.rb b/chef/cookbooks/ntp/files/default/tests/minitest/undo_test.rb new file mode 100644 index 0000000..859d84b --- /dev/null +++ b/chef/cookbooks/ntp/files/default/tests/minitest/undo_test.rb @@ -0,0 +1,18 @@ +require File.expand_path('../support/helpers', __FILE__) + +describe 'ntp::undo' do + + include Helpers::Ntp + + it 'Disables the NTP daemon' do + service(node['ntp']['service']).wont_be_running + service(node['ntp']['service']).wont_be_enabled + end + + it 'Removes the NTP packages' do + node['ntp']['packages'].each do |p| + package(p).wont_be_installed + end + end + +end diff --git a/chef/cookbooks/ntp/metadata.rb b/chef/cookbooks/ntp/metadata.rb new file mode 100644 index 0000000..d15554f --- /dev/null +++ b/chef/cookbooks/ntp/metadata.rb @@ -0,0 +1,48 @@ +name "ntp" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures ntp as a client or server" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.4.1" + +recipe "ntp", "Installs and configures ntp either as a server or client" + +%w{ ubuntu debian redhat centos fedora scientific amazon oracle freebsd windows }.each do |os| + supports os +end + +suggests "windows" + +attribute "ntp", + :display_name => "NTP", + :description => "Hash of NTP attributes", + :type => "hash" + +attribute "ntp/servers", + :display_name => "NTP Servers", + :description => "Array of servers we should talk to", + :type => "array", + :default => ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"], + :required => "recommended" + +attribute "ntp/peers", + :display_name => "NTP Peers", + :description => "Array of local NTP servers, we should peer with", + :type => "array", + :default => [], + :required => "recommended" + +attribute "ntp/restrictions", + :display_name => "Restriction lines", + :description => "Array of restriction lines to apply to NTP servers", + :type => "array", + :default => [], + :required => "recommended" + +attribute "ntp/ntpdate/disable", + :display_name => "ntpdate-debian script disable", + :description => "Defaults to false. Set to true to disable ntpdate-debian script", + :type => "string", + :default => "false", + :required => "recommended" diff --git a/chef/cookbooks/ntp/recipes/default.rb b/chef/cookbooks/ntp/recipes/default.rb new file mode 100644 index 0000000..9458dde --- /dev/null +++ b/chef/cookbooks/ntp/recipes/default.rb @@ -0,0 +1,64 @@ +# +# Cookbook Name:: ntp +# Recipe:: default +# Author:: Joshua Timberman () +# Author:: Tim Smith () +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if node['platform'] == "windows" + include_recipe "ntp::windows_client" +else + node['ntp']['packages'].each do |ntppkg| + package ntppkg + end + + [node['ntp']['varlibdir'], node['ntp']['statsdir']].each do |ntpdir| + directory ntpdir do + owner node['ntp']['var_owner'] + group node['ntp']['var_group'] + mode 00755 + end + end + + cookbook_file node['ntp']['leapfile'] do + owner node['ntp']['conf_owner'] + group node['ntp']['conf_group'] + mode 00644 + end +end + +unless node['ntp']['servers'].size > 0 + node.default['ntp']['servers'] = [ + "0.pool.ntp.org", + "1.pool.ntp.org", + "2.pool.ntp.org", + "3.pool.ntp.org" + ] + log "No NTP servers specified, using default ntp.org server pools" +end + +template node['ntp']['conffile'] do + source "ntp.conf.erb" + owner node['ntp']['conf_owner'] + group node['ntp']['conf_group'] + mode 00644 + notifies :restart, "service[#{node['ntp']['service']}]" +end + +service node['ntp']['service'] do + supports :status => true, :restart => true + action [:enable, :start] +end diff --git a/chef/cookbooks/ntp/recipes/disable.rb b/chef/cookbooks/ntp/recipes/disable.rb new file mode 100644 index 0000000..84dee95 --- /dev/null +++ b/chef/cookbooks/ntp/recipes/disable.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: ntp +# Recipe:: disable +# Author:: Guilhem Lettron +# Author:: Charles Johnson +# +# Copyright 2012, Guilhem Lettron +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +node.override['ntp']['ntpdate']['disable'] = true + +if platform_family?("debian") + include_recipe "ntp::ntpdate" +end + +service node['ntp']['service'] do + action [:disable, :stop] +end diff --git a/chef/cookbooks/ntp/recipes/ntpdate.rb b/chef/cookbooks/ntp/recipes/ntpdate.rb new file mode 100644 index 0000000..b006f82 --- /dev/null +++ b/chef/cookbooks/ntp/recipes/ntpdate.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: ntp +# Recipe:: ntpdate +# Author:: Eric G. Wolfe +# +# Copyright 2012, Eric G. Wolfe +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ntpdate is only available as a separate package on debian-based distros. +# Other distributions should use the default recipe. + +if platform_family?("debian") + package "ntpdate" + + unless node['ntp']['servers'].size > 0 + node.default['ntp']['servers'] = [ + "0.pool.ntp.org", + "1.pool.ntp.org", + "2.pool.ntp.org", + "3.pool.ntp.org" + ] + log "No NTP servers specified, using default ntp.org server pools" + end + + template "/etc/default/ntpdate" do + owner node['ntp']['conf_owner'] + group node['ntp']['conf_group'] + mode "0644" + variables( + :disable => node['ntp']['ntpdate']['disable'] + ) + end +end diff --git a/chef/cookbooks/ntp/recipes/undo.rb b/chef/cookbooks/ntp/recipes/undo.rb new file mode 100644 index 0000000..56b173f --- /dev/null +++ b/chef/cookbooks/ntp/recipes/undo.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: ntp +# Recipe:: undo +# Author:: Eric G. Wolfe +# +# Copyright 2012, Eric G. Wolfe +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +service node['ntp']['service'] do + supports :status => true, :restart => true + action [:stop, :disable] +end + +node['ntp']['packages'].each do |ntppkg| + package ntppkg do + action :remove + end +end + +ruby_block "remove ntp::undo from run list" do + block do + node.run_list.remove("recipe[ntp::undo]") + end +end diff --git a/chef/cookbooks/ntp/recipes/windows_client.rb b/chef/cookbooks/ntp/recipes/windows_client.rb new file mode 100644 index 0000000..cea768f --- /dev/null +++ b/chef/cookbooks/ntp/recipes/windows_client.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: ntp +# Recipe:: windows_client +# Author:: Tim Smith () +# +# Copyright 2012, Webtrends, Inc +# Copyright 2013, Limelight Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +directory "C:/NTP/etc" do + inherits true + action :create + recursive true +end + +cookbook_file "C:/NTP/ntp.ini" do + source "ntp.ini" + inherits true + action :create +end + +windows_package node['ntp']['vs_runtime_productname'] do + source node['ntp']['vs_runtime_url'] + options "/q" + installer_type :custom + action :install + only_if { node['kernel']['release'].to_f < 6 } +end + +if !File.exists?("C:/NTP/bin/ntpd.exe") + remote_file "#{Chef::Config[:file_cache_path]}/ntpd.exe" do + source node['ntp']['package_url'] + end + + execute "ntpd_install" do + command "#{Chef::Config[:file_cache_path]}\\ntpd.exe /USEFILE=C:\\NTP\\ntp.ini" + returns [0, 2] + end +end diff --git a/chef/cookbooks/ntp/spec/spec_helper.rb b/chef/cookbooks/ntp/spec/spec_helper.rb new file mode 100644 index 0000000..cc151d1 --- /dev/null +++ b/chef/cookbooks/ntp/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'chefspec' +require 'berkshelf' + +berks = Berkshelf::Berksfile.from_file('Berksfile').install(path: 'vendor/cookbooks/') + +# Without this line, berks will infinitely nest vendor/cookbooks/ntp on each rspec run +# https://github.com/RiotGames/berkshelf/issues/828 +require 'fileutils' +RSpec.configure do |c| + c.after(:suite) do + FileUtils.rm_rf('vendor/') + end +end diff --git a/chef/cookbooks/ntp/spec/unit/recipes/default_spec.rb b/chef/cookbooks/ntp/spec/unit/recipes/default_spec.rb new file mode 100644 index 0000000..0c01c32 --- /dev/null +++ b/chef/cookbooks/ntp/spec/unit/recipes/default_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +describe "ntp::default" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new() + runner.converge('recipe[ntp::default]') + end + + it "installs both ntp and ntpdate" do + expect(chef_run).to install_package "ntp" + expect(chef_run).to install_package "ntpdate" + end + + it "creates the varlibdir and statsdir directories" do + expect(chef_run).to create_directory '/var/lib/ntp' + directory = chef_run.directory('/var/lib/ntp') + expect(directory).to be_owned_by('ntp', 'ntp') + expect(chef_run).to create_directory '/var/log/ntpstats/' + directory = chef_run.directory('/var/log/ntpstats/') + expect(directory).to be_owned_by('ntp', 'ntp') + end + + it "creates the leapfile" do + expect(chef_run).to create_cookbook_file '/etc/ntp.leapseconds' + file = chef_run.cookbook_file('/etc/ntp.leapseconds') + expect(file).to be_owned_by('root', 'root') + end + + it "Creates the ntp.conf file" do + expect(chef_run).to create_file '/etc/ntp.conf' + file = chef_run.template('/etc/ntp.conf') + expect(file).to be_owned_by('root', 'root') + end + + it "starts and enables the ntp service" do + expect(chef_run).to start_service 'ntp' + expect(chef_run).to set_service_to_start_on_boot 'ntp' + end + +#CentOS & friends 5 get different default attributes + context "CentOS 5" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new( + platform: 'centos', + version: '5.8', + log_level: :error, + ) + Chef::Config.force_logger true + runner.converge('recipe[ntp::default]') + end + + it "installs only ntp, not ntpdate" do + expect(chef_run).to install_package "ntp" + expect(chef_run).not_to install_package "ntpdate" + end + + it "starts and enables the ntpd service" do + expect(chef_run).to start_service 'ntpd' + expect(chef_run).to set_service_to_start_on_boot 'ntpd' + end + end + +#CentOS & friends 6 get different default attributes + context "CentOS 6" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new(platform:'centos', version:'6.3') + runner.converge('recipe[ntp::default]') + end + + it "starts and enables the ntpd service" do + expect(chef_run).to start_service 'ntpd' + expect(chef_run).to set_service_to_start_on_boot 'ntpd' + end + end + +#FreeBSD gets different default attributes + context "freebsd" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new(platform: 'freebsd', version: '9.1') + runner.converge('recipe[ntp::default]') + end + + it "installs only ntp, not ntpdate" do + expect(chef_run).to install_package "ntp" + expect(chef_run).not_to install_package "ntpdate" + end + + it "creates the varlibdir and statsdir directories" do + expect(chef_run).to create_directory '/var/db' + directory = chef_run.directory('/var/db') + expect(directory).to be_owned_by('ntp', 'wheel') + expect(chef_run).to create_directory '/var/db/ntpstats' + directory = chef_run.directory('/var/db/ntpstats') + expect(directory).to be_owned_by('ntp', 'wheel') + end + + it "starts and enables the ntpd service" do + expect(chef_run).to start_service 'ntpd' + expect(chef_run).to set_service_to_start_on_boot 'ntpd' + end + + it "Creates the ntp.conf file" do + expect(chef_run).to create_file '/etc/ntp.conf' + file = chef_run.template('/etc/ntp.conf') + expect(file).to be_owned_by('root', 'wheel') + end + end + +end diff --git a/chef/cookbooks/ntp/spec/unit/recipes/disable_spec.rb b/chef/cookbooks/ntp/spec/unit/recipes/disable_spec.rb new file mode 100644 index 0000000..5d5737b --- /dev/null +++ b/chef/cookbooks/ntp/spec/unit/recipes/disable_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "ntp::disable" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new() + runner.converge('recipe[ntp::disable]') + end + + it "halts the ntp service" do + expect(chef_run).to stop_service "ntp" + expect(chef_run).to set_service_to_not_start_on_boot 'ntp' + end + + context "Ubuntu" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new(platform:'ubuntu', version:'12.04') + runner.converge('recipe[ntp::disable]') + end + + it "Disables the /etc/default/ntpdate file" do + file = chef_run.template('/etc/default/ntpdate') + expect(file).to be_owned_by('root', 'root') + expect(chef_run).to create_file_with_content '/etc/default/ntpdate', "exit 0" + end + end + +end diff --git a/chef/cookbooks/ntp/spec/unit/recipes/ntpdate_spec.rb b/chef/cookbooks/ntp/spec/unit/recipes/ntpdate_spec.rb new file mode 100644 index 0000000..4619a87 --- /dev/null +++ b/chef/cookbooks/ntp/spec/unit/recipes/ntpdate_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe "ntp::ntpdate" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new() + runner.converge('recipe[ntp::ntpdate]') + end + + it "Does not install the ntpdate package" do + expect(chef_run).not_to install_package "ntpdate" + end + + it "Does not create the /etc/default/ntpdate file" do + expect(chef_run).not_to create_file '/etc/default/ntpdate' + end + + context "Ubuntu" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new(platform:'ubuntu', version:'12.04') + runner.converge('recipe[ntp::disable]') + end + + it "Installs the ntpdate package" do + expect(chef_run).to install_package "ntpdate" + end + + it "Creates the /etc/default/ntpdate file" do + file = chef_run.template('/etc/default/ntpdate') + expect(file).to be_owned_by('root', 'root') + expect(chef_run).to create_file_with_content '/etc/default/ntpdate', "0.pool.ntp.org" + end + end + +end diff --git a/chef/cookbooks/ntp/spec/unit/recipes/undo_spec.rb b/chef/cookbooks/ntp/spec/unit/recipes/undo_spec.rb new file mode 100644 index 0000000..6f93f62 --- /dev/null +++ b/chef/cookbooks/ntp/spec/unit/recipes/undo_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe "ntp::undo" do + let(:chef_run) do + runner = ChefSpec::ChefRunner.new() + runner.converge('recipe[ntp::undo]') + end + + it "halts the ntp service" do + expect(chef_run).to stop_service "ntp" + expect(chef_run).to set_service_to_not_start_on_boot 'ntp' + end + + it "Removes the ntp packages" do + expect(chef_run).to remove_package "ntp" + expect(chef_run).to remove_package "ntpdate" + end + +end diff --git a/chef/cookbooks/ntp/spec/unit/recipes/windows_client_spec.rb b/chef/cookbooks/ntp/spec/unit/recipes/windows_client_spec.rb new file mode 100644 index 0000000..a3aca3b --- /dev/null +++ b/chef/cookbooks/ntp/spec/unit/recipes/windows_client_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "ntp::windows_client" do + let(:chef_run) do + ChefSpec::ChefRunner.new(platform: 'windows', version: '2008R2').converge('recipe[ntp::windows_client]') + end + + it "creates the c:/NTP/etc directory" do + pending "ChefSpec does not yet understand the inherits attribute in directory resources" + expect(chef_run).to create_directory('C:/NTP/etc') + end + + it "creates the c:/NTP/ntp.ini file" do + pending "ChefSpec does not yet understand the inherits attribute in cookbook_file resources" + expect(chef_run).to create_file('C:/NTP/ntp.ini') + end + + it "Fetches the ntpd.exe via remote_file" do + pending "ChefSpec does not yet understand the inherits attribute in cookbook_file resources" + expect(chef_run).to create_remote_file("#{Chef::Config[:file_cache_path]}/ntpd.exe") + end + + it "Executes the ntpd installer" do + pending "ChefSpec does not yet understand the inherits attribute in cookbook_file resources" + expect(chef_run).to execute_command("#{Chef::Config[:file_cache_path]}\\ntpd.exe /USEFILE=C:\\NTP\\ntp.ini") + end +end diff --git a/chef/cookbooks/ntp/templates/default/ntp.conf.erb b/chef/cookbooks/ntp/templates/default/ntp.conf.erb new file mode 100644 index 0000000..1405bf9 --- /dev/null +++ b/chef/cookbooks/ntp/templates/default/ntp.conf.erb @@ -0,0 +1,60 @@ +# Generated by Chef for <%= node['fqdn'] %> +# Local modifications will be overwritten. +<%# Windows OHAI does not support determining if a host is a guest %> +<% if node['platform'] != "windows" -%> +<%# See http://www.vmware.com/vmtn/resources/238 p. 23 for explanation %> +<% if node['virtualization']['role'] == "guest" -%> +tinker panic 0 +<% end -%> +statsdir <%= node['ntp']['statsdir'] %> +leapfile <%= node['ntp']['leapfile'] %> +<% end -%> +driftfile <%= node['ntp']['driftfile'] %> + +statistics loopstats peerstats clockstats +filegen loopstats file loopstats type day enable +filegen peerstats file peerstats type day enable +filegen clockstats file clockstats type day enable + +<%# If ntp.peers is not empty %> +<% unless node['ntp']['peers'].empty? -%> + <%# Loop through defined peers, but don't peer with ourself %> + <% node['ntp']['peers'].each do |ntppeer| -%> + <% if node['ipaddress'] != ntppeer and node['fqdn'] != ntppeer %> +peer <%= ntppeer %> iburst +restrict <%= ntppeer %> nomodify + <% end -%> + <% end -%> +<% end -%> + +<%# Whether this is a client or server, we want upstream servers. %> +<%# We should guard the servers array against deep merge. %> +<%# This should keep authoritative local servers from being included twice. %> +<% ( node['ntp']['servers'] - node['ntp']['peers'] ).each do |ntpserver| -%> + <%# Loop through defined servers, but don't try to upstream ourself %> + <% if node['ipaddress'] != ntpserver and node['fqdn'] != ntpserver -%> +server <%= ntpserver %> iburst +restrict <%= ntpserver %> nomodify notrap noquery + <% end -%> +<% end -%> + +restrict default kod notrap nomodify nopeer noquery +restrict 127.0.0.1 nomodify +restrict -6 default kod notrap nomodify nopeer noquery +restrict -6 ::1 nomodify + +<%# If this is a server with additional LAN restriction lines, put them here %> +<% unless node['ntp']['restrictions'].empty? -%> + <% node['ntp']['restrictions'].each do |restriction| -%> +restrict <%= restriction %> + <% end -%> +<% end -%> + +<%# It is best practice to use a high stratum undisciplined clock, if you have a real CMOS clock %> +<%# Except cases where you have a low stratum server, or a virtualized system without a real CMOS clock %> +<% if node['platform'] != "windows" -%> +<% unless node['virtualization']['role'] == "guest" -%> +server 127.127.1.0 # local clock +fudge 127.127.1.0 stratum 10 +<% end -%> +<% end -%> diff --git a/chef/cookbooks/ntp/templates/default/ntpdate.erb b/chef/cookbooks/ntp/templates/default/ntpdate.erb new file mode 100644 index 0000000..ba24b61 --- /dev/null +++ b/chef/cookbooks/ntp/templates/default/ntpdate.erb @@ -0,0 +1,14 @@ +<% if @disable %>exit 0<% end %> +# The settings in this file are used by the program ntpdate-debian, but not +# by the upstream program ntpdate. + +# Set to "yes" to take the server list from /etc/ntp.conf, from package ntp, +# so you only have to keep it in one place. +NTPDATE_USE_NTP_CONF=yes + +# List of NTP servers to use (Separate multiple servers with spaces.) +# Not used if NTPDATE_USE_NTP_CONF is yes. +NTPSERVERS="<% node['ntp']['servers'].each do |ntpserver| -%><%= ntpserver %> <% end %>" + +# Additional options to pass to ntpdate +NTPOPTIONS="" diff --git a/chef/cookbooks/ntp/test/ntp/attributes_spec.rb b/chef/cookbooks/ntp/test/ntp/attributes_spec.rb new file mode 100644 index 0000000..c2d2cf8 --- /dev/null +++ b/chef/cookbooks/ntp/test/ntp/attributes_spec.rb @@ -0,0 +1,142 @@ +# +# Cookbook Name:: ntp +# Test:: attributes_spec +# +# Author:: Fletcher Nichol +# Author:: Eric G. Wolfe +# +# Copyright 2012, Fletcher Nichol +# Copyright 2012, Eric G. Wolfe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require File.join(File.dirname(__FILE__), %w{.. support spec_helper}) +require 'chef/platform' + +describe 'Ntp::Attributes::Default' do + let(:attr_ns) { 'ntp' } + + before do + @node = Chef::Node.new + @node.consume_external_attrs(Mash.new(ohai_data), {}) + @node.from_file(File.join(File.dirname(__FILE__), %w{.. .. attributes default.rb})) + end + + describe "for unknown platform" do + let(:ohai_data) do + { :platform => "unknown", :platform_version => '3.14' } + end + + it "sets the /var/lib directory" do + @node[attr_ns]['varlibdir'].must_equal "/var/lib/ntp" + end + + it "sets the driftfile to ntp.drift" do + @node[attr_ns]['driftfile'].must_equal "/var/lib/ntp/ntp.drift" + end + + it "sets the stats directory to /var/log/ntpstats/" do + @node[attr_ns]['statsdir'].must_equal "/var/log/ntpstats/" + end + + it "sets a packages list" do + @node[attr_ns]['packages'].sort.must_equal %w{ ntp ntpdate }.sort + end + + it "sets the service name to ntp" do + @node[attr_ns]['service'].must_equal "ntp" + end + + it "sets the conf_group to root" do + @node[attr_ns]['conf_owner'].must_equal "root" + end + + it "sets the conf_group to root" do + @node[attr_ns]['conf_group'].must_equal "root" + end + + it "sets the var_user to root" do + @node[attr_ns]['var_owner'].must_equal "ntp" + end + + it "sets the var_group to root" do + @node[attr_ns]['var_group'].must_equal "ntp" + end + + it "sets the upstream server list" do + @node[attr_ns]['servers'].must_include "0.pool.ntp.org" + end + end + + describe "for centos 5 platform" do + let(:ohai_data) do + { :platform => "centos", :platform_version => '5.7' } + end + + it "sets the service name to ntpd" do + @node[attr_ns]['service'].must_equal "ntpd" + end + + it "sets a packages list" do + @node[attr_ns]['packages'].must_include "ntp" + end + end + + describe "for centos 6 platform" do + let(:ohai_data) do + { :platform => "centos", :platform_version => '6.2' } + end + + it "sets the service name to ntpd" do + @node[attr_ns]['service'].must_equal "ntpd" + end + + it "sets a packages list" do + @node[attr_ns]['packages'].sort.must_equal %w{ ntp ntpdate }.sort + end + end + + describe "for freebsd platform" do + let(:ohai_data) do + { :platform => "freebsd", :platform_version => '9.9' } + end + + it "sets the service name to ntpd" do + @node[attr_ns]['service'].must_equal "ntpd" + end + + it "sets the drift file to ntpd.drift" do + @node[attr_ns]['driftfile'].must_equal "/var/db/ntpd.drift" + end + + it "sets the var directories to /var/db" do + @node[attr_ns]['varlibdir'].must_equal "/var/db" + end + + it "sets the stats directory to /var/db/ntpstats" do + @node[attr_ns]['statsdir'].must_equal "/var/db/ntpstats" + end + + it "sets the ntp packages to ntp" do + @node[attr_ns]['packages'].must_include "ntp" + end + + it "sets the conf_group to wheel" do + @node[attr_ns]['conf_group'].must_equal "wheel" + end + + it "sets the var_group to wheel" do + @node[attr_ns]['var_group'].must_equal "wheel" + end + end +end diff --git a/chef/cookbooks/ntp/test/support/spec_helper.rb b/chef/cookbooks/ntp/test/support/spec_helper.rb new file mode 100644 index 0000000..4f4c43c --- /dev/null +++ b/chef/cookbooks/ntp/test/support/spec_helper.rb @@ -0,0 +1,3 @@ +gem 'minitest' + +require 'minitest/autorun' diff --git a/chef/cookbooks/ohai/CHANGELOG.md b/chef/cookbooks/ohai/CHANGELOG.md new file mode 100644 index 0000000..9ae0c8b --- /dev/null +++ b/chef/cookbooks/ohai/CHANGELOG.md @@ -0,0 +1,39 @@ +ohai Cookbook CHANGELOG +======================= +This file is used to list changes made in each version of the ohai cookbook. + + +v1.1.12 +------- +- Dummy release due to a Community Site upload failure + +v1.1.10 +------- +### Bug +- **[COOK-3091](https://tickets.opscode.com/browse/COOK-3091)** - Fix checking `Chef::Config[:config_file]` + +v1.1.8 +------ +- [COOK-1918] - Ohai cookbook to distribute plugins fails on windows +- [COOK-2096] - Ohai cookbook sets unix-only default path attribute + +v1.1.6 +------ +- [COOK-2057] - distribution from another cookbok fails if ohai attributes are loaded after the other cookbook + +v1.1.4 +------ +- [COOK-1128] - readme update, Replace reference to deprecated chef cookbook with one to chef-client + +v1.1.2 +------ +- [COOK-1424] - prevent plugin_path growth to infinity + +v1.1.0 +------ +- [COOK-1174] - custom_plugins is only conditionally available +- [COOK-1383] - allow plugins from other cookbooks + +v1.0.2 +------ +- [COOK-463] ohai cookbook default recipe should only reload plugins if there were updates diff --git a/chef/cookbooks/ohai/CONTRIBUTING b/chef/cookbooks/ohai/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/ohai/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/ohai/LICENSE b/chef/cookbooks/ohai/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/ohai/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/ohai/README.md b/chef/cookbooks/ohai/README.md new file mode 100644 index 0000000..46de32d --- /dev/null +++ b/chef/cookbooks/ohai/README.md @@ -0,0 +1,55 @@ +ohai Cookbook +============= +Creates a configured plugin path for distributing custom Ohai plugins, and reloads them via Ohai within the context of a Chef Client run during the compile phase (if needed). + + +Attributes +---------- +- `node['ohai']['plugin_path']` - location to drop off plugins directory, default is `/etc/chef/ohai_plugins`. This is not FHS-compliant, an FHS location would be something like `/var/lib/ohai/plugins`, or `/var/lib/chef/ohai_plugins` or similar. + + Neither an FHS location or the default value of this attribute are in the default Ohai plugin path. Set the Ohai plugin path with the config setting "`Ohai::Config[:plugin_path]`" in the Chef config file (the `chef-client::config` recipe does this automatically for you!). The attribute is not set to the default plugin path that Ohai ships with because we don't want to risk destroying existing essential plugins for Ohai. + +- `node['ohai']['plugins']` - sources of plugins, defaults to the `files/default/plugins` directory of this cookbook. You can add additional cookbooks by adding the name of the cookbook as a key and the path of the files directory as the value. You have to make sure that you don't have any file conflicts between multiple cookbooks. The last one to write wins. + + +Usage +----- +Put the recipe `ohai` at the start of the node's run list to make sure that custom plugins are loaded early on in the Chef run and data is available for later recipes. + +The execution of the custom plugins occurs within the recipe during the compile phase, so you can write new plugins and use the data they return in your Chef recipes. + +For information on how to write custom plugins for Ohai, please see the Chef wiki pages. + +http://wiki.opscode.com/display/chef/Writing+Ohai+Plugins + +*PLEASE NOTE* - This recipe reloads the Ohai plugins a 2nd time during the Chef run if: + +* The "`Ohai::Config[:plugin_path]`" config setting has *NOT* been properly set in the Chef config file +- The "`Ohai::Config[:plugin_path]`" config setting has been properly set in the Chef config file and there are updated plugins dropped off at "`node['ohai']['plugin_path']`". + + +Example +------- +For an example implementation, inspect the ohai_plugin.rb recipe in the nginx community cookbook. + + +License & Authors +----------------- +- Author:: Joshua Timberman () +- Author:: Seth Chisamore () + +```text +Copyright:: 2010-2011, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/ohai/attributes/default.rb b/chef/cookbooks/ohai/attributes/default.rb new file mode 100644 index 0000000..405a69e --- /dev/null +++ b/chef/cookbooks/ohai/attributes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: ohai +# Attribute:: default +# +# Copyright 2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# FHS location would be /var/lib/chef/ohai_plugins or similar. +case node["platform_family"] +when "windows" + default["ohai"]["plugin_path"] = "C:/chef/ohai_plugins" +else + default["ohai"]["plugin_path"] = "/etc/chef/ohai_plugins" +end + +# The list of plugins and their respective file locations +default["ohai"]["plugins"]["ohai"] = "plugins" diff --git a/chef/cookbooks/ohai/files/default/plugins/README b/chef/cookbooks/ohai/files/default/plugins/README new file mode 100644 index 0000000..72f12e3 --- /dev/null +++ b/chef/cookbooks/ohai/files/default/plugins/README @@ -0,0 +1 @@ +This directory contains custom plugins for Ohai. diff --git a/chef/cookbooks/ohai/metadata.rb b/chef/cookbooks/ohai/metadata.rb new file mode 100644 index 0000000..2d47348 --- /dev/null +++ b/chef/cookbooks/ohai/metadata.rb @@ -0,0 +1,23 @@ +name "ohai" +maintainer "Opscode, Inc" +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Distributes a directory of custom ohai plugins" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.13" + +recipe "ohai::default", "Distributes a directory of custom ohai plugins" + +attribute "ohai/plugin_path", + :display_name => "Ohai Plugin Path", + :description => "Distribute plugins to this path.", + :type => "string", + :required => "optional", + :default => "/etc/chef/ohai_plugins" + +attribute "ohai/plugins", + :display_name => "Ohai Plugin Sources", + :description => "Read plugins from these cookbooks and paths", + :type => "hash", + :required => "optional", + :default => {'ohai' => 'plugins'} diff --git a/chef/cookbooks/ohai/recipes/default.rb b/chef/cookbooks/ohai/recipes/default.rb new file mode 100644 index 0000000..1951f78 --- /dev/null +++ b/chef/cookbooks/ohai/recipes/default.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: ohai +# Recipe:: default +# +# Copyright 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +reload_ohai = false +# Add plugin_path from node attributes if missing, and ensure a reload of +# ohai in that case +unless Ohai::Config[:plugin_path].include?(node['ohai']['plugin_path']) + Ohai::Config[:plugin_path] = [node['ohai']['plugin_path'], Ohai::Config[:plugin_path]].flatten.compact + reload_ohai ||= true +end +Chef::Log.info("ohai plugins will be at: #{node['ohai']['plugin_path']}") + +# This is done during the compile phase so new plugins can be used in +# resources later in the run. +node['ohai']['plugins'].each_pair do |source_cookbook, path| + + rd = remote_directory node['ohai']['plugin_path'] do + cookbook source_cookbook + source path + mode '0755' unless platform_family?('windows') + recursive true + purge false + action :nothing + end + + rd.run_action(:create) + reload_ohai ||= rd.updated? +end + +resource = ohai 'custom_plugins' do + action :nothing +end + +# Reload ohai if the client's plugin_path did not contain +# node['ohai']['plugin_path'], or new plugins were loaded +if reload_ohai + resource.run_action(:reload) +end diff --git a/chef/cookbooks/openssl/CHANGELOG.md b/chef/cookbooks/openssl/CHANGELOG.md new file mode 100644 index 0000000..d2d85d9 --- /dev/null +++ b/chef/cookbooks/openssl/CHANGELOG.md @@ -0,0 +1,13 @@ +openssl Cookbook CHANGELOG +========================== +This file is used to list changes made in each version of the openssl cookbook. + + +v1.1.0 +------ +### Improvement +- **[COOK-3222](https://tickets.opscode.com/browse/COOK-3222)** - Allow setting length for `secure_password` + +v1.0.2 +------ +- Add name attribute to metadata diff --git a/chef/cookbooks/openssl/CONTRIBUTING b/chef/cookbooks/openssl/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/openssl/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/openssl/LICENSE b/chef/cookbooks/openssl/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/openssl/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openssl/README.md b/chef/cookbooks/openssl/README.md new file mode 100644 index 0000000..d5affff --- /dev/null +++ b/chef/cookbooks/openssl/README.md @@ -0,0 +1,38 @@ +openssl Cookbook +================ +Provide a library method to generate secure random passwords in recipes. + +Requirements +------------ +Works on any platform with OpenSSL Ruby bindings installed, which are a requirement for Chef anyway. + + +Usage +----- +Most often this will be used to generate a secure password for an attribute. + +```ruby +include Opscode::OpenSSL::Password +set_unless[:my_password] = secure_password +``` + + +License & Authors +----------------- +- Author:: Joshua Timberman () + +```text +Copyright:: 2009-2011, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/openssl/libraries/secure_password.rb b/chef/cookbooks/openssl/libraries/secure_password.rb new file mode 100644 index 0000000..37dd53e --- /dev/null +++ b/chef/cookbooks/openssl/libraries/secure_password.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: openssl +# Library:: secure_password +# Author:: Joshua Timberman +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'openssl' + +module Opscode + module OpenSSL + module Password + def secure_password(length = 20) + pw = String.new + while pw.length < length + pw << ::OpenSSL::Random.random_bytes(1).gsub(/\W/, '') + end + pw + end + end + end +end diff --git a/chef/cookbooks/openssl/metadata.rb b/chef/cookbooks/openssl/metadata.rb new file mode 100644 index 0000000..8696a07 --- /dev/null +++ b/chef/cookbooks/openssl/metadata.rb @@ -0,0 +1,9 @@ +name "openssl" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Provides a library with a method for generating secure random passwords." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.1" + +recipe "openssl", "Empty, this cookbook provides a library, see README.md" diff --git a/chef/cookbooks/openssl/recipes/default.rb b/chef/cookbooks/openssl/recipes/default.rb new file mode 100644 index 0000000..9850a28 --- /dev/null +++ b/chef/cookbooks/openssl/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Cookbook Name:: openssl +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/chef/cookbooks/openstack-block-storage/.tailor b/chef/cookbooks/openstack-block-storage/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-block-storage/Berksfile b/chef/cookbooks/openstack-block-storage/Berksfile new file mode 100644 index 0000000..bd847e9 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/Berksfile @@ -0,0 +1,8 @@ +metadata + +cookbook "openstack-image", + git: "git://github.com/stackforge/cookbook-openstack-image.git" +cookbook "openstack-identity", + git: "git://github.com/stackforge/cookbook-openstack-identity.git" +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-block-storage/Berksfile.lock b/chef/cookbooks/openstack-block-storage/Berksfile.lock new file mode 100644 index 0000000..308084b --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/Berksfile.lock @@ -0,0 +1,68 @@ +{ + "sha": "ba71763fac936d414bd4a63f004357f86f6e1bfb", + "sources": { + "openstack-block-storage": { + "locked_version": "7.0.1", + "constraint": "= 7.0.1", + "path": "." + }, + "openstack-image": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-image.git", + "ref": "c4af085fd62b542fee13d3a8a4ffdc1885ce37ed" + }, + "openstack-identity": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-identity.git", + "ref": "029fe8a648939f832f844562d0e18af2a951c783" + }, + "openstack-common": { + "locked_version": "0.3.0", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "25b183f2362fa501cfee4db331491b3d984a5c05" + }, + "apt": { + "locked_version": "2.0.0" + }, + "rabbitmq": { + "locked_version": "2.1.2" + }, + "erlang": { + "locked_version": "1.3.0", + "constraint": ">= 0.9.0" + }, + "yum": { + "locked_version": "2.3.0", + "constraint": ">= 0.5.0" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "selinux": { + "locked_version": "0.5.6" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2", + "constraint": ">= 1.3.0" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "postgresql": { + "locked_version": "3.0.2", + "constraint": ">= 1.0.0" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + }, + "python": { + "locked_version": "1.3.4" + } + } +} diff --git a/chef/cookbooks/openstack-block-storage/CHANGELOG.md b/chef/cookbooks/openstack-block-storage/CHANGELOG.md new file mode 100644 index 0000000..2762a8b --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/CHANGELOG.md @@ -0,0 +1,9 @@ +openstack-block-storage Cookbook CHANGELOG +============================== +This file is used to list changes made in each version of the openstack-block-storage cookbook. + + +v7.0.1 +------ +### Improvement +- Add audit cronjob and enable control_exchange, when metering enabled diff --git a/chef/cookbooks/openstack-block-storage/Gemfile b/chef/cookbooks/openstack-block-storage/Gemfile new file mode 100644 index 0000000..8702618 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 2.0.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-block-storage/Gemfile.lock b/chef/cookbooks/openstack-block-storage/Gemfile.lock new file mode 100644 index 0000000..4cc50ab --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/Gemfile.lock @@ -0,0 +1,223 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.13) + i18n (= 0.6.1) + multi_json (~> 1.0) + addressable (2.3.4) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.3) + activesupport (>= 3.2.0) + addressable (~> 2.3.4) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + retryable (~> 1.3.3) + ridley (~> 1.0.2) + solve (>= 0.4.4) + test-kitchen (>= 1.0.0.alpha7) + thor (~> 0.18.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.8.4) + builder (>= 2.1.2) + coderay (1.0.9) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.1) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + method_source (0.8.1) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.4) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.6) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-scp (1.1.1) + net-ssh (>= 2.6.5) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + pry (0.9.12.2) + coderay (~> 1.0.5) + method_source (~> 0.8) + slop (~> 3.4) + rack (1.5.2) + rak (1.4) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.0.2) + addressable + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + chozo (>= 0.6.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + winrm (~> 1.1.0) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rubyntlm (0.1.1) + safe_yaml (0.9.3) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + slop (3.4.5) + solve (0.4.4) + json + strainer (3.0.1) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + test-kitchen (1.0.0.alpha.7) + celluloid + mixlib-shellout + net-scp + net-ssh + pry + safe_yaml + thor + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.0) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-block-storage/README.md b/chef/cookbooks/openstack-block-storage/README.md new file mode 100644 index 0000000..af52c07 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/README.md @@ -0,0 +1,130 @@ +Description +=========== + +Installs the OpenStack Block Storage service **Cinder** as part of the OpenStack reference deployment Chef for OpenStack. The https://github.com/stackforge/openstack-chef-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Cinder is currently installed from packages. + +http://cinder.openstack.org + +Requirements +============ + +* Chef 0.10.0 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* apt +* openstack-common +* openstack-identity +* openstack-image +* selinux (Fedora) + +Usage +===== + +api +---- +- Installs the cinder-api, sets up the cinder database, + and cinder service/user/endpoints in keystone + +scheduler +---- +- Installs the cinder-scheduler service + +volume +---- +- Installs the cinder-volume service and sets up the iscsi helper + +Defaults to the ISCSI (LVM) Driver. + +Attributes +========== + +* `openstack["block-storage"]["db"]["username"]` - cinder username for database +* `openstack["block-storage"]["rabbit"]["username"]` - Username for cinder rabbit access +* `openstack["block-storage"]["rabbit"]["vhost"]` - The rabbit vhost to use +* `openstack["block-storage"]["rabbit"]["port"]` - The rabbit port to use +* `openstack["block-storage"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["block-storage"]["rabbit"]["ha"]` false). +* `openstack["block-storage"]["rabbit"]["ha"]` - Whether or not to use rabbit ha +* `openstack["block-storage"]["service_tenant_name"]` - name of tenant to use for the cinder service account in keystone +* `openstack["block-storage"]["service_user"]` - cinder service user in keystone +* `openstack["block-storage"]["service_role"]` - role for the cinder service user in keystone +* `openstack["block-storage"]["syslog"]["use"]` +* `openstack["block-storage"]["syslog"]["facility"]` +* `openstack["block-storage"]["syslog"]["config_facility"]` +* `openstack["block-storage"]["platform"]` - hash of platform specific package/service names and options +* `openstack["block-storage"]["volume"]["state_path"]` - Top-level directory for maintaining cinder's state +* `openstack["block-storage"]["volume"]["driver"]` - Driver to use for volume creation +* `openstack["block-storage"]["volume"]["volume_group"]` - Name for the VG that will contain exported volumes +* `openstack["block-storage"]["volume"]["iscsi_helper"]` - ISCSI target user-land tool to use +* `openstack["block-storage"]["rbd_pool"]` - RADOS Block Device pool to use +* `openstack["block-storage"]["rbd_user"]` - User for Cephx Authentication +* `openstack["block-storage"]["rbd_secret_uuid"]` - Secret UUID for Cephx Authentication +* `openstack["block-storage"]["policy"]["context_is_admin"]` - Define administrators +* `openstack["block-storage"]["policy"]["default"]` - default volume operations rule +* `openstack["block-storage"]["policy"]["admin_or_owner"]` - Define an admin or owner +* `openstack["block-storage"]["policy"]["admin_api"]` - Define api admin +* `openstack["block-storage"]["netapp"]["protocol"]` - how are we talking to either dfm or filer, http or https +* `openstack["block-storage"]["netapp"]["dfm_hostname"]` - Host or IP of your dfm server +* `openstack["block-storage"]["netapp"]["dfm_login"]` - Username for dfm +* `openstack["block-storage"]["netapp"]["dfm_password"]` - password for the dfm user +* `openstack["block-storage"]["netapp"]["dfm_port"]` - default port for dfm +* `openstack["block-storage"]["netapp"]["dfm_web_port"]` - web gui port for wsdl file download +* `openstack["block-storage"]["netapp"]["storage_service"]` - name of the service in dfpm +* `openstack["block-storage"]["netapp"]["netapp_server_port"]` - web admin port of the filer itself +* `openstack["block-storage"]["netapp"]["netapp_server_hostname"]` - hostname of your filer, needs to be resolvable +* `openstack["block-storage"]["netapp"]["netapp_server_login"]` - Username for netapp filer +* `openstack["block-storage"]["netapp"]["netapp_server_password"]` - password for user above +* `openstack["block-storage"]["nfs"]["shares_config"]` - file containing line by line entries of server:export +* `openstack["block-storage"]["nfs"]["mount_point_base"]` - directory to mount NFS exported shares + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Justin Shepherd () | +| **Author** | Jason Cannavale () | +| **Author** | Ron Pedde () | +| **Author** | Joseph Breu () | +| **Author** | William Kelly () | +| **Author** | Darren Birkett () | +| **Author** | Evan Callicoat () | +| **Author** | Matt Ray () | +| **Author** | Jay Pipes () | +| **Author** | John Dewey () | +| **Author** | Abel Lopez () | +| **Author** | Sean Gallagher () | +| **Author** | Ionut Artarisi () | +| | | +| **Copyright** | Copyright (c) 2012, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-block-storage/Strainerfile b/chef/cookbooks/openstack-block-storage/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-block-storage/attributes/default.rb b/chef/cookbooks/openstack-block-storage/attributes/default.rb new file mode 100644 index 0000000..31682bf --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/attributes/default.rb @@ -0,0 +1,188 @@ +# +# Cookbook Name:: openstack-block-storage +# Attributes:: default +# +# Copyright 2012, DreamHost +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +######################################################################## +# Toggles - These can be overridden at the environment level +default["developer_mode"] = false # we want secure passwords by default +######################################################################## + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["block-storage"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +default["openstack"]["block-storage"]["verbose"] = "False" +default["openstack"]["block-storage"]["debug"] = "False" + +# Default lock_path +default["openstack"]["block-storage"]["lock_path"] = "/var/lock/cinder" +# Availability zone/region for the Openstack"]["Block-Storage service +default["openstack"]["block-storage"]["region"] = "RegionOne" + +# The name of the Chef role that knows about the message queue server +# that Cinder uses +default["openstack"]["block-storage"]["rabbit_server_chef_role"] = "os-ops-messaging" + +# This is the name of the Chef role that will install the Keystone Service API +default["openstack"]["block-storage"]["keystone_service_chef_role"] = "keystone" + +# Keystone PKI signing directory. Only written to the filter:authtoken section +# of the api-paste.ini when node["openstack"]["auth"]["strategy"] == "pki" +default["openstack"]["block-storage"]["api"]["auth"]["cache_dir"] = "/var/cache/cinder/api" + +# Maximum allocatable gigabytes +# Should equal total backend storage, default is 10TB +default["openstack"]["block-storage"]["max_gigabytes"] = "10000" + +# Storage availability zone +# Default is nova +default["openstack"]["block-storage"]["storage_availability_zone"] = "nova" + +# Quota definitions +default["openstack"]["block-storage"]["quota_volumes"] = "10" +default["openstack"]["block-storage"]["quota_gigabytes"] = "1000" +default["openstack"]["block-storage"]["quota_driver"] = "cinder.quota.DbQuotaDriver" + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +default["openstack"]["block-storage"]["rabbit"]["username"] = "guest" +default["openstack"]["block-storage"]["rabbit"]["vhost"] = "/" +default["openstack"]["block-storage"]["rabbit"]["port"] = 5672 +default["openstack"]["block-storage"]["rabbit"]["host"] = "127.0.0.1" +default["openstack"]["block-storage"]["rabbit"]["ha"] = false + +default["openstack"]["block-storage"]["db"]["username"] = "cinder" + +default["openstack"]["block-storage"]["service_tenant_name"] = "service" +default["openstack"]["block-storage"]["service_user"] = "cinder" +default["openstack"]["block-storage"]["service_role"] = "admin" + +# Netapp support +default["openstack"]["block-storage"]["netapp"]["protocol"] = "http" +default["openstack"]["block-storage"]["netapp"]["dfm_hostname"] = nil +default["openstack"]["block-storage"]["netapp"]["dfm_login"] = nil +default["openstack"]["block-storage"]["netapp"]["dfm_password"] = nil +default["openstack"]["block-storage"]["netapp"]["dfm_port"] = "8088" +default["openstack"]["block-storage"]["netapp"]["dfm_web_port"] = "8080" +default["openstack"]["block-storage"]["netapp"]["storage_service"] = "storage_service" + +# Netapp direct NFS +default["openstack"]["block-storage"]["netapp"]["netapp_server_port"] = "80" +default["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"] = nil +default["openstack"]["block-storage"]["netapp"]["netapp_server_password"] = nil +default["openstack"]["block-storage"]["netapp"]["netapp_server_login"] = nil +default["openstack"]["block-storage"]["netapp"]["export"] = nil +default["openstack"]["block-storage"]["nfs"]["shares_config"] = "/etc/cinder/shares.conf" +default["openstack"]["block-storage"]["nfs"]["mount_point_base"] = "/mnt/cinder-volumes" +default["openstack"]["block-storage"]["nfs"]["nfs_disk_util"] = "df" +default["openstack"]["block-storage"]["nfs"]["nfs_sparsed_volumes"] = "true" + +# logging attribute +default["openstack"]["block-storage"]["syslog"]["use"] = false +default["openstack"]["block-storage"]["syslog"]["facility"] = "LOG_LOCAL2" +default["openstack"]["block-storage"]["syslog"]["config_facility"] = "local2" + +default["openstack"]["block-storage"]["api"]["ratelimit"] = "True" +default["openstack"]["block-storage"]["cron"]["minute"] = '00' + +default["openstack"]["block-storage"]["volume"]["state_path"] = "/var/lib/cinder" +default["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.lvm.LVMISCSIDriver" +default["openstack"]["block-storage"]["volume"]["volume_group"] = "cinder-volumes" +default["openstack"]["block-storage"]["volume"]["iscsi_helper"] = "tgtadm" +default["openstack"]["volume"]["mode"] = "loopfile" +default["openstack"]["volume"]["size"] = "3G" + + +# Ceph/RADOS options +default["openstack"]["block-storage"]["rbd_pool"] = "rbd" +default["openstack"]["block-storage"]["rbd_user"] = nil +default["openstack"]["block-storage"]["rbd_secret_uuid"] = nil + +# Cinder Policy defaults +default["openstack"]["block-storage"]["policy"]["context_is_admin"] = '["role:admin"]' +default["openstack"]["block-storage"]["policy"]["default"] = '["rule:admin_or_owner"]' +default["openstack"]["block-storage"]["policy"]["admin_or_owner"] = '["is_admin:True"], ["project_id:%(project_id)s"]' +default["openstack"]["block-storage"]["policy"]["admin_api"] = '["is_admin:True"]' + +case platform +when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this + # operating system user and group names + default["openstack"]["block-storage"]["user"] = "cinder" + default["openstack"]["block-storage"]["group"] = "cinder" + + default["openstack"]["block-storage"]["platform"] = { + "mysql_python_packages" => ["MySQL-python"], + "postgresql_python_packages" => ["python-psycopg2"], + "cinder_common_packages" => ["openstack-cinder"], + "cinder_api_packages" => ["python-cinderclient"], + "cinder_api_service" => "openstack-cinder-api", + "cinder_volume_packages" => [], + "cinder_volume_service" => "openstack-cinder-volume", + "cinder_scheduler_packages" => [], + "cinder_scheduler_service" => "openstack-cinder-scheduler", + "cinder_iscsitarget_packages" => ["scsi-target-utils"], + "cinder_iscsitarget_service" => "tgtd", + "cinder_nfs_packages" => ["nfs-utils", "nfs-utils-lib"], + "package_overrides" => "" + } +when "suse" + # operating system user and group names + default["openstack"]["block-storage"]["user"] = "openstack-cinder" + default["openstack"]["block-storage"]["group"] = "openstack-cinder" + default["openstack"]["block-storage"]["platform"] = { + "mysql_python_packages" => ["python-mysql"], + "postgresql_python_packages" => ["python-psycopg2"], + "cinder_common_packages" => ["openstack-cinder"], + "cinder_api_packages" => ["openstack-cinder-api"], + "cinder_api_service" => "openstack-cinder-api", + "cinder_scheduler_packages" => ["openstack-cinder-scheduler"], + "cinder_scheduler_service" => "openstack-cinder-scheduler", + "cinder_volume_packages" => ["openstack-cinder-volume"], + "cinder_volume_service" => "openstack-cinder-volume", + "cinder_iscsitarget_packages" => ["tgt"], + "cinder_iscsitarget_service" => "tgtd", + "cinder_nfs_packages" => ["nfs-utils"] + } +when "ubuntu" + # operating system user and group names + default["openstack"]["block-storage"]["user"] = "cinder" + default["openstack"]["block-storage"]["group"] = "cinder" + default["openstack"]["block-storage"]["platform"] = { + "mysql_python_packages" => ["python-mysqldb"], + "postgresql_python_packages" => ["python-psycopg2"], + "cinder_common_packages" => ["cinder-common"], + "cinder_api_packages" => ["cinder-api", "python-cinderclient"], + "cinder_api_service" => "cinder-api", + "cinder_volume_packages" => ["cinder-volume"], + "cinder_volume_service" => "cinder-volume", + "cinder_scheduler_packages" => ["cinder-scheduler"], + "cinder_scheduler_service" => "cinder-scheduler", + "cinder_iscsitarget_packages" => ["tgt"], + "cinder_iscsitarget_service" => "tgt", + "cinder_nfs_packages" => ["nfs-common"], + "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh b/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh new file mode 100644 index 0000000..045c7f1 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/files/default/cinder-volumes.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# This file is going to create a loop file as volume disk. +# + +vgdisplay |grep cinder-volumes; +if [ $? -ne 0 ]; then + dd if=/dev/zero of=/mnt/cinder-volumes bs=1 count=0 seek=2G + losetup /dev/loop0 /mnt/cinder-volumes + pvcreate /dev/loop0 + vgcreate cinder-volumes /dev/loop0 +fi diff --git a/chef/cookbooks/openstack-block-storage/metadata.rb b/chef/cookbooks/openstack-block-storage/metadata.rb new file mode 100644 index 0000000..b8a4a53 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/metadata.rb @@ -0,0 +1,24 @@ +name "openstack-block-storage" +maintainer "AT&T Services, Inc." +maintainer_email "cookbooks@lists.tfoundry.com" +license "Apache 2.0" +description "The OpenStack Advanced Volume Management service Cinder." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.1" + +recipe "openstack-block-storage::common", "Defines the common pieces of repeated code from the other recipes" +recipe "openstack-block-storage::api", "Installs the cinder-api, sets up the cinder database, and cinder service/user/endpoints in keystone" +recipe "openstack-block-storage::keystone_registration", "Registers cinder service/user/endpoints in keystone" +recipe "openstack-block-storage::scheduler", "Installs the cinder-scheduler service" +recipe "openstack-block-storage::volume", "Installs the cinder-volume service and sets up the iscsi helper" + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "apt" +depends "openstack-common", "~> 0.4.0" +depends "openstack-identity", "~> 7.0.0" +depends "openstack-image", "~> 7.0.0" +depends "selinux" +depends "python" diff --git a/chef/cookbooks/openstack-block-storage/recipes/api.rb b/chef/cookbooks/openstack-block-storage/recipes/api.rb new file mode 100644 index 0000000..72387f3 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/api.rb @@ -0,0 +1,88 @@ +# +# Cookbook Name:: openstack-block-storage +# Recipe:: api +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# Copyright 2013, SUSE Linux Gmbh. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-block-storage::cinder-common" + +platform_options = node["openstack"]["block-storage"]["platform"] + +platform_options["cinder_api_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +db_type = node['openstack']['db']['volume']['db_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +directory ::File.dirname(node["openstack"]["block-storage"]["api"]["auth"]["cache_dir"]) do + owner node["openstack"]["block-storage"]["user"] + group node["openstack"]["block-storage"]["group"] + mode 00700 +end + +service "cinder-api" do + service_name platform_options["cinder_api_service"] + supports :status => true, :restart => true + + action :enable + subscribes :restart, "template[/etc/cinder/cinder.conf]" +end + +identity_admin_endpoint = endpoint "identity-admin" +service_tenant_name = node['openstack']['identity']['volume']['tenant'] +service_user = node['openstack']['identity']['volume']['username'] +service_pass = service_password node['openstack']['identity']['volume']['password'] + +execute "cinder-manage db sync" + +template "/etc/cinder/api-paste.ini" do + source "api-paste.ini.erb" + group node["openstack"]["block-storage"]["group"] + owner node["openstack"]["block-storage"]["user"] + mode 00644 + variables( + :identity_admin_endpoint => identity_admin_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + + notifies :restart, "service[cinder-api]", :immediately +end + +template "/etc/cinder/policy.json" do + source "policy.json.erb" + owner node["openstack"]["block-storage"]["user"] + group node["openstack"]["block-storage"]["group"] + mode 00644 + notifies :restart, "service[cinder-api]" +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb b/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb new file mode 100644 index 0000000..c4ad818 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/cinder-common.rb @@ -0,0 +1,63 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +if node["openstack"]["block-storage"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options = node["openstack"]["block-storage"]["platform"] + +platform_options["cinder_common_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +db_user = node["openstack"]["db"]["volume"]["username"] +db_pass = db_password node["openstack"]["db"]["volume"]["password"] +sql_connection = db_uri("volume", db_user, db_pass) + +if node["openstack"]["block-storage"]["rabbit"]["ha"] + rabbit_hosts = node['openstack']['mq']['bind_address'] +end +rabbit_pass = user_password node['openstack']['mq']['password'] + +glance_api_endpoint = endpoint "image-api" + +directory "/etc/cinder" do + group node["openstack"]["block-storage"]["group"] + owner node["openstack"]["block-storage"]["user"] + mode 00750 + action :create +end + +template "/etc/cinder/cinder.conf" do + source "cinder.conf.erb" + group node["openstack"]["block-storage"]["group"] + owner node["openstack"]["block-storage"]["user"] + mode 00644 + variables( + :sql_connection => sql_connection, + :rabbit_password => rabbit_pass, + :rabbit_hosts => rabbit_hosts, + :glance_host => glance_api_endpoint.host, + :glance_port => glance_api_endpoint.port + ) +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/default.rb b/chef/cookbooks/openstack-block-storage/recipes/default.rb new file mode 100644 index 0000000..e42044e --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: openstack-block-storage +# Recipe:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb b/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb new file mode 100644 index 0000000..c6aef22 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/identity_registration.rb @@ -0,0 +1,85 @@ +# +# Cookbook Name:: openstack-block-storage +# Recipe:: identity_registration +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" +bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +auth_uri = ::URI.decode identity_admin_endpoint.to_s +cinder_api_endpoint = endpoint "volume-api" +service_pass = service_password node['openstack']['identity']['volume']['password'] +region = node["openstack"]["block-storage"]["region"] +service_tenant_name = node['openstack']['identity']['volume']['tenant'] +service_user = node['openstack']['identity']['volume']['username'] +service_role = node['openstack']['identity']['volume']['role'] + +openstack_identity_register "Register Cinder Volume Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "cinder" + service_type "volume" + service_description "Cinder Volume Service" + endpoint_region region + endpoint_adminurl ::URI.decode cinder_api_endpoint.to_s + endpoint_internalurl ::URI.decode cinder_api_endpoint.to_s + endpoint_publicurl ::URI.decode cinder_api_endpoint.to_s + + action :create_service +end + +openstack_identity_register "Register Cinder Volume Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "cinder" + service_type "volume" + service_description "Cinder Volume Service" + endpoint_region region + endpoint_adminurl ::URI.decode cinder_api_endpoint.to_s + endpoint_internalurl ::URI.decode cinder_api_endpoint.to_s + endpoint_publicurl ::URI.decode cinder_api_endpoint.to_s + + action :create_endpoint +end + +openstack_identity_register "Register Cinder Service User" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + user_enabled true # Not required as this is the default + + action :create_user +end + +openstack_identity_register "Grant service Role to Cinder Service User for Cinder Service Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb b/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb new file mode 100644 index 0000000..ddd56e3 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/scheduler.rb @@ -0,0 +1,64 @@ +# +# Cookbook Name:: openstack-block-storage +# Recipe:: scheduler +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# Copyright 2013, SUSE Linux Gmbh. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-block-storage::cinder-common" + +platform_options = node["openstack"]["block-storage"]["platform"] + +platform_options["cinder_scheduler_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +# FIXME this can be removed if/when 1:2013.1-0ubuntu2 makes it into precise +#if platform?("ubuntu") && (node["platform_version"].to_f == 12.04) +# include_recipe "python" +# python_pip "stevedore" do +# action :upgrade +# end +#end + +db_type = node['openstack']['db']['volume']['db_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +service "cinder-scheduler" do + service_name platform_options["cinder_scheduler_service"] + supports :status => true, :restart => true + + action [ :enable, :start ] + subscribes :restart, "template[/etc/cinder/cinder.conf]" +end + +if node["openstack"]["metering"] + cron "cinder-volume-usage-audit" do + command "cinder-volume-usage-audit > /var/log/cinder/audit.log 2>&1" + action :create + user node["openstack"]["block-storage"]["user"] + end +end diff --git a/chef/cookbooks/openstack-block-storage/recipes/volume.rb b/chef/cookbooks/openstack-block-storage/recipes/volume.rb new file mode 100644 index 0000000..9ff4019 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/recipes/volume.rb @@ -0,0 +1,129 @@ +# +# Cookbook Name:: openstack-block-storage +# Recipe:: volume +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# Copyright 2013, SUSE Linux Gmbh. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-block-storage::cinder-common" + +platform_options = node["openstack"]["block-storage"]["platform"] + +platform_options["cinder_volume_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +db_type = node['openstack']['db']['volume']['db_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +platform_options["cinder_iscsitarget_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +execute "create_cinder_volumes" do + command "sh /tmp/cinder_volumes.sh" + action :nothing +end + +case node["openstack"]["block-storage"]["volume"]["driver"] + when "cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver" + node.override["openstack"]["block-storage"]["netapp"]["dfm_password"] = service_password "netapp" + + when "cinder.volume.drivers.RBDDriver" + node.override["openstack"]["block-storage"]["rbd_secret_uuid"] = service_password "rbd" + + when "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + node.override["openstack"]["block-storage"]["netapp"]["netapp_server_password"] = service_password "netapp-filer" + + directory node["openstack"]["block-storage"]["nfs"]["mount_point_base"] do + owner node["openstack"]["block-storage"]["user"] + group node["openstack"]["block-storage"]["group"] + action :create + end + + template node["openstack"]["block-storage"]["nfs"]["shares_config"] do + source "shares.conf.erb" + mode "0600" + owner node["openstack"]["block-storage"]["user"] + group node["openstack"]["block-storage"]["group"] + variables( + "host" => node["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"], + "export" => node["openstack"]["block-storage"]["netapp"]["export"] + ) + notifies :restart, "service[cinder-volume]" + end + + platform_options["cinder_nfs_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end + end + + when "cinder.volume.drivers.lvm.LVMISCSIDriver" + template "/tmp/cinder_volumes.sh" do + source "cinder_volumes.sh.erb" + owner "root" + group "root" + mode 00755 + variables( + :volumesize => node["openstack"]["volume"]["size"] + ) + notifies :run, "execute[create_cinder_volumes]", :delayed + only_if { node["openstack"]["volume"]["mode"] == "loopfile" } + end +end + +service "cinder-volume" do + service_name platform_options["cinder_volume_service"] + supports :status => true, :restart => true + + action [ :enable, :restart ] + subscribes :restart, "template[/etc/cinder/cinder.conf]" +end + +service "iscsitarget" do + service_name platform_options["cinder_iscsitarget_service"] + supports :status => true, :restart => true + + action :enable +end + +template "/etc/tgt/targets.conf" do + source "targets.conf.erb" + mode 00600 + + notifies :restart, "service[iscsitarget]", :immediately +end diff --git a/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb new file mode 100644 index 0000000..bcba515 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/api-opensuse_spec.rb @@ -0,0 +1,38 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::api" do + before { block_storage_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-block-storage::api" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "openstack-cinder-api" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysql" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysql" + end + + it "starts cinder api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-api" + end + + expect_creates_policy_json( + "service[cinder-api]", "openstack-cinder", "openstack-cinder") + expect_creates_cinder_conf( + "service[cinder-api]", "openstack-cinder", "openstack-cinder") + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb new file mode 100644 index 0000000..1e38cbc --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/api-redhat_spec.rb @@ -0,0 +1,33 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::api" do + before { block_storage_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-block-storage::api" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "python-cinderclient" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "MySQL-python" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "MySQL-python" + end + + it "starts cinder api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-api" + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/api_spec.rb b/chef/cookbooks/openstack-block-storage/spec/api_spec.rb new file mode 100644 index 0000000..c88335e --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/api_spec.rb @@ -0,0 +1,139 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::api" do + before { block_storage_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::api" + end + + expect_runs_openstack_common_logging_recipe + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "cinder-api" + expect(@chef_run).to upgrade_package "python-cinderclient" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysqldb" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysqldb" + end + + describe "/var/cache/cinder" do + before do + @dir = @chef_run.directory "/var/cache/cinder" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "cinder", "cinder" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + it "starts cinder api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "cinder-api" + end + + expect_creates_cinder_conf "service[cinder-api]", "cinder", "cinder" + + describe "cinder.conf" do + before do + @file = "/etc/cinder/cinder.conf" + end + + it "runs logging recipe if node attributes say to" do + expect(@chef_run).to create_file_with_content @file, + "log_config = /etc/openstack/logging.conf" + end + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).not_to create_file_with_content @file, + "log_config = /etc/openstack/logging.conf" + end + + it "has rbd driver settings" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"] = { + "driver" => "cinder.volume.drivers.RBDDriver" + } + end + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).to create_file_with_content @file, + /^rbd_/ + expect(chef_run).not_to create_file_with_content @file, + /^netapp_/ + end + + it "has netapp driver settings" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"] = { + "driver" => "cinder.volume.drivers.netapp.NetAppISCSIDriver" + } + end + chef_run.converge "openstack-block-storage::api" + + expect(chef_run).to create_file_with_content @file, + /^netapp_/ + expect(chef_run).not_to create_file_with_content @file, + /^rbd_/ + end + end + + it "runs db migrations" do + cmd = "cinder-manage db sync" + + expect(@chef_run).to execute_command cmd + end + + expect_creates_policy_json "service[cinder-api]", "cinder", "cinder" + + describe "api-paste.ini" do + before do + @file = @chef_run.template "/etc/cinder/api-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "cinder", "cinder" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has signing_dir" do + expect(@chef_run).to create_file_with_content @file.name, + "signing_dir = /var/cache/cinder/api" + end + + it "notifies cinder-api restart" do + expect(@file).to notify "service[cinder-api]", :restart + end + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb new file mode 100644 index 0000000..dcbca9c --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common-opensuse_spec.rb @@ -0,0 +1,18 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::cinder-common" do + before { block_storage_stubs } + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["openstack"]["mq"] = { + "host" => "127.0.0.1" + } + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::cinder-common" + end + + it "installs the openstack-cinder package" do + expect(@chef_run).to upgrade_package "openstack-cinder" + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb new file mode 100644 index 0000000..ad6ce9d --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common-redhat_spec.rb @@ -0,0 +1,18 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::cinder-common" do + before { block_storage_stubs } + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| + n.set["openstack"]["mq"] = { + "host" => "127.0.0.1" + } + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::cinder-common" + end + + it "installs the openstack-cinder package" do + expect(@chef_run).to upgrade_package "openstack-cinder" + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb b/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb new file mode 100644 index 0000000..6e88b4f --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/cinder_common_spec.rb @@ -0,0 +1,110 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::cinder-common" do + before { block_storage_stubs } + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["mq"] = { + "host" => "127.0.0.1" + } + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::cinder-common" + end + + it "installs the cinder-common package" do + expect(@chef_run).to upgrade_package "cinder-common" + end + + describe "/etc/cinder" do + before do + @dir = @chef_run.directory "/etc/cinder" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "cinder", "cinder" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "750" + end + end + + describe "cinder.conf" do + before do + @file = @chef_run.template "/etc/cinder/cinder.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "cinder", "cinder" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has rabbit_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_hosts" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_hosts=" + end + + it "does not have rabbit_ha_queues" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_ha_queues=" + end + + it "has rabbit_port" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_port=5672" + end + + it "has rabbit_userid" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_userid=guest" + end + + it "has rabbit_password" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_password=rabbit-pass" + end + + it "has rabbit_virtual_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_virtual_host=/" + end + + describe "rabbit ha" do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["block-storage"]["rabbit"]["ha"] = true + end + @chef_run.converge "openstack-block-storage::cinder-common" + end + + it "has rabbit_hosts" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" + end + + it "has rabbit_ha_queues" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_ha_queues=True" + end + + it "does not have rabbit_host" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_port" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_port=5672" + end + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/default_spec.rb b/chef/cookbooks/openstack-block-storage/spec/default_spec.rb new file mode 100644 index 0000000..3a77cb4 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/default_spec.rb @@ -0,0 +1,4 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::default" do +end diff --git a/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb new file mode 100644 index 0000000..e6ed35c --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/identity_registration_spec.rb @@ -0,0 +1,82 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::identity_registration" do + before do + block_storage_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-block-storage::identity_registration" + end + + it "registers cinder volume service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Cinder Volume Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "https://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_name => "cinder", + :service_type => "volume", + :service_description => "Cinder Volume Service", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :endpoint_internalurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :endpoint_publicurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :action => [:create_service] + ) + end + + it "registers cinder volume endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Cinder Volume Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "https://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_name => "cinder", + :service_type => "volume", + :service_description => "Cinder Volume Service", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :endpoint_internalurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :endpoint_publicurl => "https://127.0.0.1:8776/v1/%(tenant_id)s", + :action => [:create_endpoint] + ) + end + + it "registers service user" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Cinder Service User" + ).to_hash + + expect(resource).to include( + :auth_uri => "https://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "cinder", + :user_pass => "cinder-pass", + :user_enabled => true, + :action => [:create_user] + ) + end + + it "grants admin role to service user for service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Grant service Role to Cinder Service User for Cinder Service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "https://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "cinder", + :role_name => "admin", + :action => [:grant_role] + ) + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb new file mode 100644 index 0000000..42de173 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler-opensuse_spec.rb @@ -0,0 +1,44 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::scheduler" do + before { block_storage_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-block-storage::scheduler" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "openstack-cinder-scheduler" + end + + it "does not upgrade stevedore" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).not_to upgrade_python_pip "stevedore" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysql" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysql" + end + + it "starts cinder scheduler" do + expect(@chef_run).to start_service "openstack-cinder-scheduler" + end + + it "starts cinder scheduler on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-scheduler" + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb new file mode 100644 index 0000000..e72d9f7 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler-redhat_spec.rb @@ -0,0 +1,44 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::scheduler" do + before { block_storage_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-block-storage::scheduler" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "openstack-cinder" + end + + it "does not upgrade stevedore" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).not_to upgrade_python_pip "stevedore" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "MySQL-python" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "MySQL-python" + end + + it "starts cinder scheduler" do + expect(@chef_run).to start_service "openstack-cinder-scheduler" + end + + it "starts cinder scheduler on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-cinder-scheduler" + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb b/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb new file mode 100644 index 0000000..bb4ceea --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/scheduler_spec.rb @@ -0,0 +1,62 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::scheduler" do + before { block_storage_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::scheduler" + end + + expect_runs_openstack_common_logging_recipe + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + it "installs cinder api packages" do + expect(@chef_run).to upgrade_package "cinder-scheduler" + end + + it "upgrades stevedore" do + expect(@chef_run).to upgrade_python_pip "stevedore" + end + + it "does not upgrade stevedore" do + opts = ::UBUNTU_OPTS.merge(:version => "10.04") + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).not_to upgrade_python_pip "stevedore" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysqldb" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::scheduler" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysqldb" + end + + it "starts cinder scheduler" do + expect(@chef_run).to start_service "cinder-scheduler" + end + + it "starts cinder scheduler on boot" do + expect(@chef_run).to set_service_to_start_on_boot "cinder-scheduler" + end + + expect_creates_cinder_conf "service[cinder-scheduler]", "cinder", "cinder" + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb b/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb new file mode 100644 index 0000000..8b5024d --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/spec_helper.rb @@ -0,0 +1,81 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def block_storage_stubs + ::Chef::Recipe.any_instance.stub(:rabbit_servers). + and_return "1.1.1.1:5672,2.2.2.2:5672" + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "#{node['openstack']['identity']['admin_token']}"). + and_return "bootstrap-token" + ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password). + with("guest"). + and_return "rabbit-pass" + ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:service_password). + with("openstack-block-storage"). + and_return "cinder-pass" +end + +def expect_runs_openstack_common_logging_recipe + it "runs logging recipe if node attributes say to" do + expect(@chef_run).to include_recipe "openstack-common::logging" + end +end + +def expect_creates_cinder_conf service, user, group, action=:restart + describe "cinder.conf" do + before do + @file = @chef_run.template "/etc/cinder/cinder.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by user, group + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "notifies service restart" do + expect(@file).to notify service, action + end + end +end + +def expect_creates_policy_json service, user, group, action=:restart + describe "policy.json" do + before do + @file = @chef_run.template "/etc/cinder/policy.json" + end + + it "has proper owner" do + expect(@file).to be_owned_by user, group + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "notifies service restart" do + expect(@file).to notify service, action + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb new file mode 100644 index 0000000..e5b7537 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/volume-opensuse_spec.rb @@ -0,0 +1,65 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::volume" do + before { block_storage_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-block-storage::volume" + end + + it "installs cinder volume packages" do + expect(@chef_run).to upgrade_package "openstack-cinder-volume" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysql" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysql" + end + + it "installs cinder iscsi packages" do + expect(@chef_run).to upgrade_package "tgt" + end + + it "starts cinder volume" do + expect(@chef_run).to start_service "openstack-cinder-volume" + end + + it "starts cinder volume on boot" do + expected = "openstack-cinder-volume" + expect(@chef_run).to set_service_to_start_on_boot expected + end + + it "starts iscsi target on boot" do + expect(@chef_run).to set_service_to_start_on_boot "tgtd" + end + + it "installs nfs packages" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + end + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "nfs-utils" + expect(chef_run).not_to upgrade_package "nfs-utils-lib" + end + + it "has opensuse include" do + file = "/etc/tgt/targets.conf" + + expect(@chef_run).to create_file_with_content file, + "include /var/lib/cinder/volumes/*" + expect(@chef_run).not_to create_file_with_content file, + "include /etc/tgt/conf.d/*.conf" + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb new file mode 100644 index 0000000..0638f82 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/volume-redhat_spec.rb @@ -0,0 +1,61 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::volume" do + before { block_storage_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-block-storage::volume" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "MySQL-python" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "MySQL-python" + end + + it "installs cinder iscsi packages" do + expect(@chef_run).to upgrade_package "scsi-target-utils" + end + + it "starts cinder volume" do + expect(@chef_run).to start_service "openstack-cinder-volume" + end + + it "starts cinder volume on boot" do + expected = "openstack-cinder-volume" + expect(@chef_run).to set_service_to_start_on_boot expected + end + + it "starts iscsi target on boot" do + expect(@chef_run).to set_service_to_start_on_boot "tgtd" + end + + it "installs nfs packages" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + end + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "nfs-utils" + expect(chef_run).to upgrade_package "nfs-utils-lib" + end + + it "has redhat include" do + file = "/etc/tgt/targets.conf" + + expect(@chef_run).to create_file_with_content file, + "include /var/lib/cinder/volumes/*" + expect(@chef_run).not_to create_file_with_content file, + "include /etc/tgt/conf.d/*.conf" + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb b/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb new file mode 100644 index 0000000..7360791 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/spec/volume_spec.rb @@ -0,0 +1,121 @@ +require_relative "spec_helper" + +describe "openstack-block-storage::volume" do + before { block_storage_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-block-storage::volume" + end + + expect_runs_openstack_common_logging_recipe + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + it "installs cinder volume packages" do + expect(@chef_run).to upgrade_package "cinder-volume" + end + + it "installs mysql python packages by default" do + expect(@chef_run).to upgrade_package "python-mysqldb" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["db"]["volume"]["db_type"] = "postgresql" + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "python-psycopg2" + expect(chef_run).not_to upgrade_package "python-mysqldb" + end + + it "installs cinder iscsi packages" do + expect(@chef_run).to upgrade_package "tgt" + end + + it "installs nfs packages" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + end + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to upgrade_package "nfs-common" + end + + it "creates the nfs mount point" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" + end + chef_run.converge "openstack-block-storage::volume" + + expect(chef_run).to create_directory "/mnt/cinder-volumes" + end + + it "configures netapp dfm password" do + ::Chef::Recipe.any_instance.stub(:service_password).with("netapp"). + and_return "netapp-pass" + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver" + end + chef_run.converge "openstack-block-storage::volume" + n = chef_run.node["openstack"]["block-storage"]["netapp"]["dfm_password"] + + expect(n).to eq "netapp-pass" + end + + it "configures rbd password" do + ::Chef::Recipe.any_instance.stub(:service_password).with("rbd"). + and_return "rbd-pass" + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["block-storage"]["volume"]["driver"] = "cinder.volume.drivers.RBDDriver" + end + chef_run.converge "openstack-block-storage::volume" + n = chef_run.node["openstack"]["block-storage"]["rbd_secret_uuid"] + + expect(n).to eq "rbd-pass" + end + + it "starts cinder volume" do + expect(@chef_run).to start_service "cinder-volume" + end + + it "starts cinder volume on boot" do + expect(@chef_run).to set_service_to_start_on_boot "cinder-volume" + end + + expect_creates_cinder_conf "service[cinder-volume]", "cinder", "cinder" + + it "starts iscsi target on boot" do + expect(@chef_run).to set_service_to_start_on_boot "tgt" + end + + describe "targets.conf" do + before do + @file = @chef_run.template "/etc/tgt/targets.conf" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "notifies iscsi restart" do + expect(@file).to notify "service[iscsitarget]", :restart + end + + it "has ubuntu include" do + expect(@chef_run).to create_file_with_content @file.name, + "include /etc/tgt/conf.d/*.conf" + expect(@chef_run).not_to create_file_with_content @file.name, + "include /var/lib/cinder/volumes/*" + end + end + end +end diff --git a/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb new file mode 100644 index 0000000..ac144e7 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/api-paste.ini.erb @@ -0,0 +1,61 @@ +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> + +############# +# Openstack # +############# + +[composite:osapi_volume] +use = call:cinder.api:root_app_factory +/: apiversions +/v1: openstack_volume_api_v1 +/v2: openstack_volume_api_v2 + +[composite:openstack_volume_api_v1] +use = call:cinder.api.middleware.auth:pipeline_factory +noauth = faultwrap sizelimit noauth apiv1 +keystone = faultwrap sizelimit authtoken keystonecontext apiv1 +keystone_nolimit = faultwrap sizelimit authtoken keystonecontext apiv1 + +[composite:openstack_volume_api_v2] +use = call:cinder.api.middleware.auth:pipeline_factory +noauth = faultwrap sizelimit noauth apiv2 +keystone = faultwrap sizelimit authtoken keystonecontext apiv2 +keystone_nolimit = faultwrap sizelimit authtoken keystonecontext apiv2 + +[filter:faultwrap] +paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory + +[filter:noauth] +paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory + +[filter:sizelimit] +paste.filter_factory = cinder.api.middleware.sizelimit:RequestBodySizeLimiter.factory + +[app:apiv1] +paste.app_factory = cinder.api.v1.router:APIRouter.factory + +[app:apiv2] +paste.app_factory = cinder.api.v2.router:APIRouter.factory + +[pipeline:apiversions] +pipeline = faultwrap osvolumeversionapp + +[app:osvolumeversionapp] +paste.app_factory = cinder.api.versions:Versions.factory + +########## +# Shared # +########## + +[filter:keystonecontext] +paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory + +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["block-storage"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb new file mode 100644 index 0000000..fc07768 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/cinder.conf.erb @@ -0,0 +1,805 @@ +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> + +[DEFAULT] + +######## defined in cinder.openstack.common.cfg:CommonConfigOpts ######## + +debug=<%= node["openstack"]["block-storage"]["debug"] %> +#### (BoolOpt) Print debugging output + +verbose=<%= node["openstack"]["block-storage"]["verbose"] %> +#### (BoolOpt) Print more verbose output + +# lock_path +lock_path=<%= node["openstack"]["block-storage"]["lock_path"] %> +notification_driver=cinder.openstack.common.notifier.rpc_notifier + +# log_config= +#### (StrOpt) If this option is specified, the logging configuration file +#### specified is used and overrides any other logging options +#### specified. Please see the Python logging module +#### documentation for details on logging configuration files. + +# log_format=%(asctime)s %(levelname)8s [%(name)s] %(message)s +#### (StrOpt) A logging.Formatter log message format string which may use +#### any of the available logging.LogRecord attributes. Default: +#### %default + +# log_date_format=%Y-%m-%d %H:%M:%S +#### (StrOpt) Format string for %(asctime)s in log records. Default: +#### %default + +# log_file= +#### (StrOpt) (Optional) Name of log file to output to. If not set, +#### logging will go to stdout. + +# log_dir= +#### (StrOpt) (Optional) The directory to keep log files in (will be +#### prepended to --logfile) + +# ================= Syslog Options ============================ + +<% if node["openstack"]["block-storage"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> + +######## defined in cinder.flags ######## + +# connection_type= +#### (StrOpt) Virtualization api connection type : libvirt, xenapi, or +#### fake + +sql_connection=<%= @sql_connection %> +# sql_connection=sqlite:///$state_path/$sqlite_db +#### (StrOpt) The SQLAlchemy connection string used to connect to the +#### database + +# sql_connection_debug=0 +#### (IntOpt) Verbosity of SQL debugging information. 0=None, +#### 100=Everything + +# api_paste_config=api-paste.ini +#### (StrOpt) File name for the paste.deploy config for cinder-api + +# pybasedir=/usr/lib/python/site-packages +#### (StrOpt) Directory where the cinder python module is installed + +# bindir=$pybasedir/bin +#### (StrOpt) Directory where cinder binaries are installed + +state_path=<%= node["openstack"]["block-storage"]["volume"]["state_path"] %> +#### (StrOpt) Top-level directory for maintaining cinder's state + +my_ip=<%= node["ipaddress"] %> +#### (StrOpt) ip address of this host + +glance_host=<%= @glance_host %> +#### (StrOpt) default glance hostname or ip + +glance_port=<%= @glance_port %> +#### (IntOpt) default glance port + +# glance_api_servers=$glance_host:$glance_port +#### (ListOpt) A list of the glance api servers available to cinder +#### ([hostname|ip]:port) + +# glance_num_retries=0 +#### (IntOpt) Number retries when downloading an image from glance + +# scheduler_topic=cinder-scheduler +#### (StrOpt) the topic scheduler nodes listen on + +# volume_topic=cinder-volume +#### (StrOpt) the topic volume nodes listen on + +api_rate_limit=<%= node["openstack"]["block-storage"]["api"]["ratelimit"] %> +#### (BoolOpt) whether to rate limit the api + +# osapi_volume_ext_list= +#### (ListOpt) Specify list of extensions to load when using +#### osapi_volume_extension option with +#### cinder.api.openstack.volume.contrib.select_extensions + +# osapi_volume_extension=cinder.api.openstack.volume.contrib.standard_extensions +#### (MultiStrOpt) osapi volume extension to load + +# osapi_compute_link_prefix= +#### (StrOpt) Base URL that will be presented to users in links to the +#### OpenStack Compute API + +# osapi_max_limit=1000 +#### (IntOpt) the maximum number of items returned in a single response +#### from a collection resource + +# sqlite_db=cinder.sqlite +#### (StrOpt) the filename to use with sqlite + +# sqlite_synchronous=true +#### (BoolOpt) If passed, use synchronous mode for sqlite + +# sql_idle_timeout=3600 +#### (IntOpt) timeout before idle sql connections are reaped + +# sql_max_retries=10 +#### (IntOpt) maximum db connection retries during startup. (setting -1 +#### implies an infinite retry count) + +# sql_retry_interval=10 +#### (IntOpt) interval between retries of opening a sql connection + +# volume_manager=cinder.volume.manager.VolumeManager +#### (StrOpt) full class name for the Manager for volume + +# scheduler_manager=cinder.scheduler.manager.SchedulerManager +#### (StrOpt) full class name for the Manager for scheduler + +# host=cinder +#### (StrOpt) Name of this node. This can be an opaque identifier. It is +#### not necessarily a hostname, FQDN, or IP address. + +storage_availability_zone=<%= node["openstack"]["block-storage"]["storage_availability_zone"] %> +#### (StrOpt) availability zone of this node + +# memcached_servers= +#### (ListOpt) Memcached servers or None for in process cache. + +# volume_usage_audit_period=month +#### (StrOpt) time period to generate volume usages for. Time period must +#### be hour, day, month or year + +# root_helper=cinder-rootwrap +#### (StrOpt) Deprecated: command to use for running commands as root + +rootwrap_config=/etc/cinder/rootwrap.conf +#### (StrOpt) Path to the rootwrap configuration file to use for running +#### commands as root + +# monkey_patch=false +#### (BoolOpt) Whether to log monkey patching + +# monkey_patch_modules= +#### (ListOpt) List of modules/decorators to monkey patch + +# service_down_time=60 +#### (IntOpt) maximum time since last check-in for up service + +# volume_api_class=cinder.volume.api.API +#### (StrOpt) The full class name of the volume API class to use + +auth_strategy=keystone +#### (StrOpt) The strategy to use for auth. Supports noauth, keystone, and +#### deprecated. + +<% if node["openstack"]["block-storage"]["rabbit"]["control_exchange"] %> +control_exchange=<%=node["openstack"]["block-storage"]["rabbit"]["control_exchange"]%> +<% end %> +# control_exchange=cinder +#### (StrOpt) AMQP exchange to connect to if using RabbitMQ or Qpid + + +######## defined in cinder.policy ######## + +# policy_file=policy.json +#### (StrOpt) JSON file representing policy + +# policy_default_rule=default +#### (StrOpt) Rule checked when requested rule is not found + + +######## defined in cinder.quota ######## + +quota_volumes=<%= node["openstack"]["block-storage"]["quota_volumes"] %> +#### (IntOpt) number of volumes allowed per project + +quota_gigabytes=<%= node["openstack"]["block-storage"]["quota_gigabytes"] %> +#### (IntOpt) number of volume gigabytes allowed per project + +# reservation_expire=86400 +#### (IntOpt) number of seconds until a reservation expires + +# until_refresh=0 +#### (IntOpt) count of reservations until usage is refreshed + +# max_age=0 +#### (IntOpt) number of seconds between subsequent usage refreshes + +quota_driver=<%= node["openstack"]["block-storage"]["quota_driver"] %> +#### (StrOpt) default driver to use for quota checks + + +######## defined in cinder.service ######## + +# report_interval=10 +#### (IntOpt) seconds between nodes reporting state to datastore + +# periodic_interval=60 +#### (IntOpt) seconds between running periodic tasks + +# periodic_fuzzy_delay=60 +#### (IntOpt) range of seconds to randomly delay when starting the +#### periodic task scheduler to reduce stampeding. (Disable by +#### setting to 0) + +# osapi_volume_listen=0.0.0.0 +#### (StrOpt) IP address for OpenStack Volume API to listen + +# osapi_volume_listen_port=8776 +#### (IntOpt) port for os volume api to listen + + +######## defined in cinder.test ######## + +# sqlite_clean_db=clean.sqlite +#### (StrOpt) File name of clean sqlite db + +# fake_tests=true +#### (BoolOpt) should we use everything for testing + + +######## defined in cinder.api.auth ######## + +# use_forwarded_for=false +#### (BoolOpt) Treat X-Forwarded-For as the canonical remote address. Only +#### enable this if you have a sanitizing proxy. + + +######## defined in cinder.api.sizelimit ######## + +# osapi_max_request_body_size=114688 +#### (IntOpt) Max size for body of a request + + +######## defined in cinder.common.deprecated ######## + +# fatal_deprecations=false +#### (BoolOpt) make deprecations fatal + + +######## defined in cinder.db.api ######## + +db_backend=sqlalchemy +#### (StrOpt) The backend to use for db + +# enable_new_services=true +#### (BoolOpt) Services to be added to the available pool on create + +# volume_name_template=volume-%s +#### (StrOpt) Template string to be used to generate volume names + +# snapshot_name_template=snapshot-%s +#### (StrOpt) Template string to be used to generate snapshot names + + +######## defined in cinder.db.base ######## + +# db_driver=cinder.db +#### (StrOpt) driver to use for database access + + +######## defined in cinder.openstack.common.log ######## + +# logdir= +#### (StrOpt) Log output to a per-service log file in named directory + +# logfile= +#### (StrOpt) Log output to a named file + +# use_stderr=true +#### (BoolOpt) Log output to standard error + +# logfile_mode=0644 +#### (StrOpt) Default file mode used when creating log files + +# logging_context_format_string=%(asctime)s %(levelname)s %(name)s [%(request_id)s %(user_id)s %(project_id)s] %(instance)s%(message)s +#### (StrOpt) format string to use for log messages with context + +# logging_default_format_string=%(asctime)s %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s +#### (StrOpt) format string to use for log messages without context + +# logging_debug_format_suffix=%(funcName)s %(pathname)s:%(lineno)d +#### (StrOpt) data to append to log format when level is DEBUG + +# logging_exception_prefix=%(asctime)s %(process)d TRACE %(name)s %(instance)s +#### (StrOpt) prefix each line of exception output with this format + +# default_log_levels=amqplib=WARN,sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO,eventlet.wsgi.server=WARN +#### (ListOpt) list of logger=LEVEL pairs + +# publish_errors=false +#### (BoolOpt) publish error events + +# instance_format="[instance: %(uuid)s] " +#### (StrOpt) If an instance is passed with the log message, format it +#### like this + +# instance_uuid_format="[instance: %(uuid)s] " +#### (StrOpt) If an instance UUID is passed with the log message, format +#### it like this + + +######## defined in cinder.openstack.common.notifier.api ######## + +#### (MultiStrOpt) Driver or drivers to handle sending notifications + +# default_notification_level=INFO +#### (StrOpt) Default notification level for outgoing notifications + +# default_publisher_id=$host +#### (StrOpt) Default publisher_id for outgoing notifications + + +######## defined in cinder.openstack.common.notifier.rabbit_notifier ######## + +# notification_topics=notifications +#### (ListOpt) AMQP topic used for openstack notifications + + +######## defined in cinder.openstack.common.rpc ######## + +# rpc_backend=cinder.openstack.common.rpc.impl_kombu +#### (StrOpt) The messaging module to use, defaults to kombu. + +# rpc_thread_pool_size=64 +#### (IntOpt) Size of RPC thread pool + +# rpc_conn_pool_size=30 +#### (IntOpt) Size of RPC connection pool + +# rpc_response_timeout=60 +#### (IntOpt) Seconds to wait for a response from call or multicall + +# rpc_cast_timeout=30 +#### (IntOpt) Seconds to wait before a cast expires (TTL). Only supported +#### by impl_zmq. + +# allowed_rpc_exception_modules=cinder.openstack.common.exception,nova.exception,cinder.exception +#### (ListOpt) Modules of exceptions that are permitted to be recreatedupon +#### receiving exception data from an rpc call. + +# fake_rabbit=false +#### (BoolOpt) If passed, use a fake RabbitMQ provider + + +######## defined in cinder.openstack.common.rpc.impl_kombu ######## + +# kombu_ssl_version= +#### (StrOpt) SSL version to use (valid only if SSL enabled) + +# kombu_ssl_keyfile= +#### (StrOpt) SSL key file (valid only if SSL enabled) + +# kombu_ssl_certfile= +#### (StrOpt) SSL cert file (valid only if SSL enabled) + +# kombu_ssl_ca_certs= +#### (StrOpt) SSL certification authority file (valid only if SSL enabled) + +<% if node["openstack"]["block-storage"]["rabbit"]["ha"] -%> +rabbit_hosts=<%= @rabbit_hosts %> +#### (ListOpt) RabbitMQ HA cluster host:port pairs + +# rabbit_durable_queues=false +#### (BoolOpt) use durable queues in RabbitMQ + +rabbit_ha_queues=True +#### (BoolOpt) use H/A queues in RabbitMQ (x-ha-policy: all).You need to +#### wipe RabbitMQ database when changing this option. +<% else -%> +# rabbit_host=<%= node["openstack"]["block-storage"]["rabbit"]["host"] %> +rabbit_host=<%= node['openstack']['mq']['bind_address'] %> + +#### (StrOpt) The RabbitMQ broker address where a single node is used +# rabbit_port=<%= node["openstack"]["block-storage"]["rabbit"]["port"] %> +rabbit_port=<%= node['openstack']['mq']['port'] %> + +#### (IntOpt) The RabbitMQ broker port where a single node is used +<% end -%> + +# rabbit_use_ssl=false +#### (BoolOpt) connect over SSL for RabbitMQ + +rabbit_userid=<%= node["openstack"]["block-storage"]["rabbit"]["username"] %> +#### (StrOpt) the RabbitMQ userid + +rabbit_password=<%= @rabbit_password %> +#### (StrOpt) the RabbitMQ password + +rabbit_virtual_host=<%= node["openstack"]["block-storage"]["rabbit"]["vhost"] %> +#### (StrOpt) the RabbitMQ virtual host + +# rabbit_retry_interval=1 +#### (IntOpt) how frequently to retry connecting with RabbitMQ + +# rabbit_retry_backoff=2 +#### (IntOpt) how long to backoff for between retries when connecting to +#### RabbitMQ + +# rabbit_max_retries=0 +#### (IntOpt) maximum retries with trying to connect to RabbitMQ (the +#### default of 0 implies an infinite retry count) + + +######## defined in cinder.openstack.common.rpc.impl_qpid ######## + +# qpid_hostname=localhost +#### (StrOpt) Qpid broker hostname + +# qpid_port=5672 +#### (StrOpt) Qpid broker port + +# qpid_username= +#### (StrOpt) Username for qpid connection + +# qpid_password= +#### (StrOpt) Password for qpid connection + +# qpid_sasl_mechanisms= +#### (StrOpt) Space separated list of SASL mechanisms to use for auth + +# qpid_reconnect=true +#### (BoolOpt) Automatically reconnect + +# qpid_reconnect_timeout=0 +#### (IntOpt) Reconnection timeout in seconds + +# qpid_reconnect_limit=0 +#### (IntOpt) Max reconnections before giving up + +# qpid_reconnect_interval_min=0 +#### (IntOpt) Minimum seconds between reconnection attempts + +# qpid_reconnect_interval_max=0 +#### (IntOpt) Maximum seconds between reconnection attempts + +# qpid_reconnect_interval=0 +#### (IntOpt) Equivalent to setting max and min to the same value + +# qpid_heartbeat=60 +#### (IntOpt) Seconds between connection keepalive heartbeats + +# qpid_protocol=tcp +#### (StrOpt) Transport to use, either 'tcp' or 'ssl' + +# qpid_tcp_nodelay=true +#### (BoolOpt) Disable Nagle algorithm + + +######## defined in cinder.openstack.common.rpc.impl_zmq ######## + +# rpc_zmq_bind_address=* +#### (StrOpt) ZeroMQ bind address. Should be a wildcard (*), an ethernet +#### interface, or IP. The "host" option should point or resolve +#### to this address. + +# rpc_zmq_matchmaker=cinder.openstack.common.rpc.matchmaker.MatchMakerLocalhost +#### (StrOpt) MatchMaker driver + +# rpc_zmq_port=9501 +#### (IntOpt) ZeroMQ receiver listening port + +# rpc_zmq_port_pub=9502 +#### (IntOpt) ZeroMQ fanout publisher port + +# rpc_zmq_contexts=1 +#### (IntOpt) Number of ZeroMQ contexts, defaults to 1 + +# rpc_zmq_ipc_dir=/var/run/openstack +#### (StrOpt) Directory for holding IPC sockets + +# rpc_zmq_host=cinder +#### (StrOpt) Name of this node. Must be a valid hostname, FQDN, or IP +#### address. Must match "host" option, if running Nova. + + +######## defined in cinder.openstack.common.rpc.matchmaker ######## + +# matchmaker_ringfile=/etc/nova/matchmaker_ring.json +#### (StrOpt) Matchmaker ring file (JSON) + + +######## defined in cinder.scheduler.driver ######## + +# scheduler_host_manager=cinder.scheduler.host_manager.HostManager +#### (StrOpt) The scheduler host manager class to use + + +######## defined in cinder.scheduler.manager ######## + +# scheduler_driver=cinder.scheduler.simple.SimpleScheduler +#### (StrOpt) Default driver to use for the scheduler + + +######## defined in cinder.scheduler.simple ######## + +max_gigabytes=<%= node["openstack"]["block-storage"]["max_gigabytes"] %> +#### (IntOpt) maximum number of volume gigabytes to allow per host + + +######## defined in cinder.volume.api ######## + +# snapshot_same_host=true +#### (BoolOpt) Create volume from snapshot at the host where snapshot +#### resides + + +######## defined in cinder.volume.driver ######## + +volume_group=<%= node["openstack"]["block-storage"]["volume"]["volume_group"] %> +#### (StrOpt) Name for the VG that will contain exported volumes + +# num_shell_tries=3 +#### (IntOpt) number of times to attempt to run flakey shell commands + +# num_iscsi_scan_tries=3 +#### (IntOpt) number of times to rescan iSCSI target to find volume + +# iscsi_num_targets=100 +#### (IntOpt) Number of iscsi target ids per host + +# iscsi_target_prefix=iqn.2010-10.org.openstack: +#### (StrOpt) prefix for iscsi volumes + +# iscsi_ip_address=$my_ip +#### (StrOpt) use this ip for iscsi + +# iscsi_port=3260 +#### (IntOpt) The port that the iSCSI daemon is listening on +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.RBDDriver" %> +rbd_pool=<%= node["openstack"]["block-storage"]["rbd_pool"] %> +#### (StrOpt) the RADOS pool in which rbd volumes are stored + +rbd_user=<%= node["openstack"]["block-storage"]["rbd_user"] %> +#### (StrOpt) the RADOS client name for accessing rbd volumes + +rbd_secret_uuid=<%= node["openstack"]["block-storage"]["rbd_secret_uuid"] %> +#### (StrOpt) the libvirt uuid of the secret for the rbd_uservolumes +<% end %> +# volume_tmp_dir= +#### (StrOpt) where to store temporary image files if the volume driver +#### does not write them directly to the volume + + +######## defined in cinder.volume.iscsi ######## + +iscsi_helper=<%= node["openstack"]["block-storage"]["volume"]["iscsi_helper"] %> +#### (StrOpt) iscsi target user-land tool to use + +# volumes_dir=$state_path/volumes +#### (StrOpt) Volume configuration file storage directory + + +######## defined in cinder.volume.manager ######## + +volume_driver=<%= node["openstack"]["block-storage"]["volume"]["driver"] %> +#### (StrOpt) Driver to use for volume creation + +# use_local_volumes=true +#### (BoolOpt) if True, will not discover local volumes + +# volume_force_update_capabilities=false +#### (BoolOpt) if True will force update capabilities on each check + + +######## defined in cinder.volume.netapp ######## + +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.netapp.NetAppISCSIDriver" %> +netapp_wsdl_url=<%= node["openstack"]["block-storage"]["netapp"]["protocol"] %>://<%= node["openstack"]["block-storage"]["netapp"]["dfm_hostname"] %>:<%= node["openstack"]["block-storage"]["netapp"]["dfm_web_port"] %>/dfm.wsdl +#### (StrOpt) URL of the WSDL file for the DFM server + +netapp_login=<%= node["openstack"]["block-storage"]["netapp"]["dfm_login"] %> +#### (StrOpt) User name for the DFM server + +netapp_password=<%= node["openstack"]["block-storage"]["netapp"]["dfm_password"] %> +#### (StrOpt) Password for the DFM server + +netapp_server_hostname=<%= node["openstack"]["block-storage"]["netapp"]["dfm_hostname"] %> +#### (StrOpt) Hostname for the DFM server + +netapp_server_port=<%= node["openstack"]["block-storage"]["netapp"]["dfm_port"] %> +#### (IntOpt) Port number for the DFM server + +netapp_storage_service=<%= node["openstack"]["block-storage"]["netapp"]["storage_service"] %> +#### (StrOpt) Storage service to use for provisioning (when +#### volume_type=None) + +# netapp_storage_service_prefix= +#### (StrOpt) Prefix of storage service name to use for provisioning +#### (volume_type name will be appended) + +# netapp_vfiler= +#### (StrOpt) Vfiler to use for provisioning +<% end %> + +######## defined in cinder.volume.netapp_nfs ######## +<% if node["openstack"]["block-storage"]["volume"]["driver"] == "cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver" %> + +nfs_mount_point_base=<%= node["openstack"]["block-storage"]["nfs"]["mount_point_base"] %> +<% node["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"].each do |h| %> +netapp_server_hostname=<%= h %> +<% end %> +netapp_server_port=<%= node["openstack"]["block-storage"]["netapp"]["netapp_server_port"] %> +netapp_login=<%= node["openstack"]["block-storage"]["netapp"]["netapp_server_login"] %> +netapp_password=<%= node["openstack"]["block-storage"]["netapp"]["netapp_server_password"] %> +nfs_shares_config=<%= node["openstack"]["block-storage"]["nfs"]["shares_config"] %> +nfs_disk_util=<%= node["openstack"]["block-storage"]["nfs"]["nfs_disk_util"] %> +nfs_sparsed_volumes=<%= node["openstack"]["block-storage"]["nfs"]["nfs_sparsed_volumes"] %> + +<% end %> + +######## defined in cinder.volume.nexenta.volume ######## + +# nexenta_host= +#### (StrOpt) IP address of Nexenta SA + +# nexenta_rest_port=2000 +#### (IntOpt) HTTP port to connect to Nexenta REST API server + +# nexenta_rest_protocol=auto +#### (StrOpt) Use http or https for REST connection (default auto) + +# nexenta_user=admin +#### (StrOpt) User name to connect to Nexenta SA + +# nexenta_password=nexenta +#### (StrOpt) Password to connect to Nexenta SA + +# nexenta_iscsi_target_portal_port=3260 +#### (IntOpt) Nexenta target portal port + +# nexenta_volume=cinder +#### (StrOpt) pool on SA that will hold all volumes + +# nexenta_target_prefix=iqn.1986-03.com.sun:02:cinder- +#### (StrOpt) IQN prefix for iSCSI targets + +# nexenta_target_group_prefix=cinder/ +#### (StrOpt) prefix for iSCSI target groups on SA + +# nexenta_blocksize= +#### (StrOpt) block size for volumes (blank=default,8KB) + +# nexenta_sparse=false +#### (BoolOpt) flag to create sparse volumes + + +######## defined in cinder.volume.nfs ######## + +# nfs_shares_config= +#### (StrOpt) File with the list of available nfs shares + +# nfs_mount_point_base=$state_path/mnt +#### (StrOpt) Base dir where nfs expected to be mounted + +# nfs_disk_util=df +#### (StrOpt) Use du or df for free space calculation + +# nfs_sparsed_volumes=true +#### (BoolOpt) Create volumes as sparsed files which take no space.If set +#### to False volume is created as regular file.In such case +#### volume creation takes a lot of time. + + +######## defined in cinder.volume.san ######## + +# san_thin_provision=true +#### (BoolOpt) Use thin provisioning for SAN volumes? + +# san_ip= +#### (StrOpt) IP address of SAN controller + +# san_login=admin +#### (StrOpt) Username for SAN controller + +# san_password= +#### (StrOpt) Password for SAN controller + +# san_private_key= +#### (StrOpt) Filename of private key to use for SSH authentication + +# san_clustername= +#### (StrOpt) Cluster name to use for creating volumes + +# san_ssh_port=22 +#### (IntOpt) SSH port to use with SAN + +# san_is_local=false +#### (BoolOpt) Execute commands locally instead of over SSH; use if the +#### volume service is running on the SAN device + +# san_zfs_volume_base=rpool/ +#### (StrOpt) The ZFS path under which to create zvols for volumes. + + +######## defined in cinder.volume.solidfire ######## + +# sf_emulate_512=true +#### (BoolOpt) Set 512 byte emulation on volume creation; + +# sf_mvip= +#### (StrOpt) IP address of SolidFire MVIP + +# sf_login=admin +#### (StrOpt) Username for SF Cluster Admin + +# sf_password= +#### (StrOpt) Password for SF Cluster Admin + +# sf_allow_tenant_qos=true +#### (BoolOpt) Allow tenants to specify QOS on create + + +######## defined in cinder.volume.storwize_svc ######## + +# storwize_svc_volpool_name=volpool +#### (StrOpt) Storage system storage pool for volumes + +# storwize_svc_vol_rsize=2% +#### (StrOpt) Storage system space-efficiency parameter for volumes + +# storwize_svc_vol_warning=0 +#### (StrOpt) Storage system threshold for volume capacity warnings + +# storwize_svc_vol_autoexpand=true +#### (BoolOpt) Storage system autoexpand parameter for volumes (True/False) + +# storwize_svc_vol_grainsize=256 +#### (StrOpt) Storage system grain size parameter for volumes +#### (32/64/128/256) + +# storwize_svc_vol_compression=false +#### (BoolOpt) Storage system compression option for volumes + +# storwize_svc_vol_easytier=true +#### (BoolOpt) Enable Easy Tier for volumes + +# storwize_svc_flashcopy_timeout=120 +#### (StrOpt) Maximum number of seconds to wait for FlashCopy to be +#### prepared. Maximum value is 600 seconds (10 minutes). + + +######## defined in cinder.volume.xiv ######## + +# xiv_proxy=xiv_openstack.nova_proxy.XIVNovaProxy +#### (StrOpt) Proxy driver + + +######## defined in cinder.volume.zadara ######## + +# zadara_vpsa_ip= +#### (StrOpt) Management IP of Zadara VPSA + +# zadara_vpsa_port= +#### (StrOpt) Zadara VPSA port number + +# zadara_vpsa_use_ssl=false +#### (BoolOpt) Use SSL connection + +# zadara_user= +#### (StrOpt) User name for the VPSA + +# zadara_password= +#### (StrOpt) Password for the VPSA + +# zadara_vpsa_poolname= +#### (StrOpt) Name of VPSA storage pool for volumes + +# zadara_default_cache_policy=write-through +#### (StrOpt) Default cache policy for volumes + +# zadara_default_encryption=NO +#### (StrOpt) Default encryption policy for volumes + +# zadara_default_striping_mode=simple +#### (StrOpt) Default striping mode for volumes + +# zadara_default_stripesize=64 +#### (StrOpt) Default stripe size for volumes + +# zadara_vol_name_template=OS_%s +#### (StrOpt) Default template for VPSA volume names + +# zadara_vpsa_auto_detach_on_delete=true +#### (BoolOpt) Automatically detach from servers on volume delete + +# zadara_vpsa_allow_nonexistent_delete=true +#### (BoolOpt) Don't halt on deletion of non-existing volumes diff --git a/chef/cookbooks/openstack-block-storage/templates/default/cinder_volumes.sh.erb b/chef/cookbooks/openstack-block-storage/templates/default/cinder_volumes.sh.erb new file mode 100644 index 0000000..666ed51 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/cinder_volumes.sh.erb @@ -0,0 +1,12 @@ +#!/bin/bash +# This file is going to create a loop file as volume disk. +# + +vgdisplay |grep cinder-volumes +if [ $? -ne 0 ]; then + dd if=/dev/zero of=/mnt/cinder-volumes bs=1 count=0 seek=<%=@volumesize %> + losetup /dev/loop0 /mnt/cinder-volumes + pvcreate /dev/loop0 + vgcreate cinder-volumes /dev/loop0 +fi + diff --git a/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb b/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb new file mode 100644 index 0000000..fc07b12 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/policy.json.erb @@ -0,0 +1,34 @@ +{ + "context_is_admin": [<%= node["openstack"]["block-storage"]["policy"]["context_is_admin"] %>], + "admin_or_owner": [<%= node["openstack"]["block-storage"]["policy"]["admin_or_owner"] %>], + "default": [<%= node["openstack"]["block-storage"]["policy"]["default"] %>], + + "admin_api": [<%= node["openstack"]["block-storage"]["policy"]["admin_api"] %>], + + "volume:create": [], + "volume:get_all": [], + "volume:get_volume_metadata": [], + "volume:get_snapshot": [], + "volume:get_all_snapshots": [], + + "volume_extension:types_manage": [["rule:admin_api"]], + "volume_extension:types_extra_specs": [["rule:admin_api"]], + "volume_extension:extended_snapshot_attributes": [], + "volume_extension:volume_image_metadata": [], + + "volume_extension:quotas:show": [], + "volume_extension:quotas:update_for_project": [["rule:admin_api"]], + "volume_extension:quotas:update_for_user": [["rule:admin_or_projectadmin"]], + "volume_extension:quota_classes": [], + + "volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]], + "volume_extension:snapshot_admin_actions:reset_status": [["rule:admin_api"]], + "volume_extension:volume_admin_actions:force_delete": [["rule:admin_api"]], + "volume_extension:snapshot_admin_actions:force_delete": [["rule:admin_api"]], + + "volume_extension:volume_host_attribute": [["rule:admin_api"]], + "volume_extension:volume_tenant_attribute": [["rule:admin_api"]], + "volume_extension:hosts": [["rule:admin_api"]], + "volume_extension:services": [["rule:admin_api"]], + "volume:services": [["rule:admin_api"]] +} diff --git a/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb new file mode 100644 index 0000000..e8b93f2 --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/shares.conf.erb @@ -0,0 +1,4 @@ +# Automatically generated by chef, changes will be overwritten +<% node["openstack"]["block-storage"]["netapp"]["netapp_server_hostname"].each do |h| %> +<%= h %>:<%= @export %> +<% end %> diff --git a/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb b/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb new file mode 100644 index 0000000..fedbd7f --- /dev/null +++ b/chef/cookbooks/openstack-block-storage/templates/default/targets.conf.erb @@ -0,0 +1,9 @@ +<%= node["openstack"]["block-storage"]["custom_template_banner"] %> + +<% if %w{redhat centos fedora suse}.include?(node["platform"]) %> +include /var/lib/cinder/volumes/* +<% end %> +<% if %w{debian ubuntu}.include?(node["platform"]) %> +include /etc/tgt/conf.d/*.conf +<% end %> +default-driver iscsi diff --git a/chef/cookbooks/openstack-common/.tailor b/chef/cookbooks/openstack-common/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-common/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-common/Berksfile b/chef/cookbooks/openstack-common/Berksfile new file mode 100644 index 0000000..850a120 --- /dev/null +++ b/chef/cookbooks/openstack-common/Berksfile @@ -0,0 +1 @@ +metadata diff --git a/chef/cookbooks/openstack-common/CHANGELOG.md b/chef/cookbooks/openstack-common/CHANGELOG.md new file mode 100644 index 0000000..ee9e599 --- /dev/null +++ b/chef/cookbooks/openstack-common/CHANGELOG.md @@ -0,0 +1,76 @@ +# CHANGELOG for cookbook-openstack-common + +This file is used to list changes made in each version of cookbook-openstack-common. + +## 0.4.3: +* Corrected `#search_for` role and recipe queries. + +## 0.4.2: +* Remove hardcoded localhost for mysql host specification. + +## 0.4.1: +* Changed endpoint attributes to use http for default scheme. this is inline with + default settings in keystone. fine for dev, but should be ssl for prod. + +## 0.4.0: +* Remove `#config_by_role` as it is no longer used and no longer suits our needs. + +## 0.3.5: +* Reverted change made in 8311869e5b99fecefd567ce3f1ad1cbdf8d5c5c6. + +## 0.3.4: +* Allow `#search_for` to always returns an array. + +## 0.3.3: +* Incorrectly mocked search results, as a result `#search_for` was performing unnecessary + actions to an array. + +## 0.3.2: +* Fix network-api endpoint path + +## 0.3.1: +* Corrected a faulty Chef search query with `#config_by_role`. The search returns a + Hash, not an array. + +## 0.3.0: +* Added `#rabbit_servers` method, which returns a comma-delimited string of rabbit + servers in the format of host:port. +* The `#memcached_servers` method no longer accepts an environment. +* Re-factored methods which search to a generic `#search_for`. +* Added `#address_for` method, which returns the IPv4 (default) address of the given + interface. +* Added global mysql setting of port and db type, for use with wrapper cookbooks. +* Add default messaging attributes, for use with wrapper cookbooks. + +## 0.2.6: +* Update Chef dependency to Chef 11. + +## 0.2.5: +* Moved the default library to database, to better represent its duties. + +## 0.2.4: +* Break out #memcached_servers into separate library. + +## 0.2.3: +* Sort the results returned by #memcached_servers. + +## 0.2.2: +* Provides a mechanism to override memcache_servers search logic through node attributes. + +## 0.2.1: +* Adds a prettytable_to_array function for parsing OpenStack CLI output. + +## 0.2.0: +* First release of cookbook-openstack-common that aligns with the Grizzly packaging. +* Adds OpenStack Network endpoints. + +## 0.1.x: +* Folsom-based packaging. + +## 0.0.1: +* Initial release of cookbook-openstack-common. + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/chef/cookbooks/openstack-common/Gemfile b/chef/cookbooks/openstack-common/Gemfile new file mode 100644 index 0000000..7de4657 --- /dev/null +++ b/chef/cookbooks/openstack-common/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 1.4.5" +gem "chefspec", "~> 1.2.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-common/Gemfile.lock b/chef/cookbooks/openstack-common/Gemfile.lock new file mode 100644 index 0000000..08e2917 --- /dev/null +++ b/chef/cookbooks/openstack-common/Gemfile.lock @@ -0,0 +1,207 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.13) + i18n (= 0.6.1) + multi_json (~> 1.0) + addressable (2.3.4) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (1.4.5) + activesupport (>= 3.2.0) + addressable + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + json (>= 1.5.0) + minitar + mixlib-config (~> 1.1) + mixlib-shellout (~> 1.1) + multi_json (~> 1.5) + retryable + ridley (~> 0.12.4) + solve (>= 0.4.2) + thor (~> 0.18.0) + yajl-ruby + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.2.0) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.8.4) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.8.1) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.1) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.4) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.6) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nokogiri (1.5.9) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rak (1.4) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (0.12.4) + addressable + celluloid (~> 0.14.0) + chozo (>= 0.6.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + mixlib-authentication (>= 1.3.0) + mixlib-config (>= 1.1.0) + mixlib-log (>= 1.3.0) + mixlib-shellout (>= 1.1.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + winrm (~> 1.1.0) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.4.4) + json + strainer (2.1.0) + berkshelf (~> 1.3) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.0) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 1.4.5) + chef (~> 11.4.4) + chefspec (~> 1.2.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-common/LICENSE b/chef/cookbooks/openstack-common/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/chef/cookbooks/openstack-common/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/chef/cookbooks/openstack-common/README.md b/chef/cookbooks/openstack-common/README.md new file mode 100644 index 0000000..641e596 --- /dev/null +++ b/chef/cookbooks/openstack-common/README.md @@ -0,0 +1,190 @@ +Description +=========== + +This cookbook provides common setup recipes, helper methods and attributes that describe an OpenStack deployment as part of the OpenStack reference deployment Chef for OpenStack. + +Requirements +============ + +* Chef 0.10.0 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* apt +* database + +Attributes +========== + +Please see the extensive inline documentation in `attributes/default.rb` for descriptions +of all the settable attributes for this cookbook. + +Note that all attributes are in the `default["openstack"]` "namespace" + +Libraries +========= + +This cookbook exposes a set of default library routines: + +* `endpoint` -- Used to return a `::URI` object representing the named OpenStack endpoint +* `endpoints` -- Useful for operating on all OpenStack endpoints +* `db` -- Returns a Hash of information about a named OpenStack database +* `db_uri` -- Returns the SQLAlchemy RFC-1738 DB URI (see: http://rfc.net/rfc1738.html) for a named OpenStack database +* `db_create_with_user` -- Creates a database and database user for a named OpenStack database +* `secret` -- Returns the value of an encrypted data bag for a named OpenStack secret key and key-section +* `db_password` -- Ease-of-use helper that returns the decrypted database password for a named OpenStack database +* `service_password` -- Ease-of-use helper that returns the decrypted service password for named OpenStack service +* `user_password` -- Ease-of-use helper that returns the decrypted password for a Keystone user + +Usage +----- + +default +---- + +Installs/Configures common recipes + +```json +"run_list": [ + "recipe[openstack-common]" +] +``` + +logging +---- + +Installs/Configures common logging + +```json +"run_list": [ + "recipe[openstack-common::logging]" +] +``` + +The following are code examples showing the above library routines in action. +Remember when using the library routines exposed by this library to include +the Openstack routines in your recipe's `::Chef::Recipe` namespace, like so: + +```ruby +class ::Chef::Recipe + include ::Openstack +end +``` + +Example of using the `endpoint` routine: + +```ruby +nova_api_ep = endpoint "compute-api" +::Chef::Log.info("Using Openstack Compute API endpoint at #{nova_api_ep.to_s}") + +# Note that endpoint URIs may contain variable interpolation markers such +# as `%(tenant_id)s`, so you may need to decode them. Do so like this: + +require "uri" + +puts ::URI.decode nova_api_ap.to_s +``` + +Example of using the `db_password` and `db_uri` routine: + +```ruby +db_pass = db_password "cinder" +db_user = node["cinder"]["db"]["user"] +sql_connection = db_uri "volume", db_user, db_pass + +template "/etc/cinder/cinder.conf" do + source "cinder.conf.erb" + owner node["cinder"]["user"] + group node["cinder"]["group"] + mode 00644 + variables( + "sql_connection" => sql_connection + ) +end +``` + +URI Operations +-------------- + +Use the `Openstack::uri_from_hash` routine to helpfully return a `::URI::Generic` +object for a hash that contains any of the following keys: + +* `host` +* `uri` +* `port` +* `path` +* `scheme` + +If the `uri` key is in the hash, that will be used as the URI, otherwise the URI will be +constructed from the various parts of the hash corresponding to the keys above. + +```ruby +# Suppose node hash contains the following subhash in the :identity_service key: +# { +# :host => 'identity.example.com', +# :port => 5000, +# :scheme => 'https' +# } +uri = ::Openstack::uri_from_hash(node[:identity_service]) +# uri.to_s would == "https://identity.example.com:5000" +``` + +The routine will return nil if neither a `uri` or `host` key exists in the supplied hash. + +Using the library without prefixing with ::Openstack +---------------------------------------------------- + +Don't like prefixing calls to the library's routines with `::Openstack`? Do this: + +```ruby +class ::Chef::Recipe + include ::Openstack +end +``` + +in your recipe. + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Jay Pipes () | +| **Author** | John Dewey () | +| **Author** | Matt Ray () | +| **Author** | Craig Tracey () | +| **Author** | Sean Gallagher () | +| **Author** | Ionut Artarisi () | +| | | +| **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, Craig Tracey | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-common/Strainerfile b/chef/cookbooks/openstack-common/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-common/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-common/attributes/default.rb b/chef/cookbooks/openstack-common/attributes/default.rb new file mode 100644 index 0000000..60210e4 --- /dev/null +++ b/chef/cookbooks/openstack-common/attributes/default.rb @@ -0,0 +1,417 @@ +# +# Cookbook Name:: openstack-common +# Attributes:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Setting this to True means that database passwords and service user +# passwords for Keystone will be easy-to-remember values -- they will be +# the same value as the key. For instance, if a cookbook calls the +# ::Openstack::secret routine like so: +# +# pass = secret "passwords", "nova" +# +# The value of pass will be "nova" +ENV['http_proxy']=Chef::Config.http_proxy +ENV['https_proxy']=Chef::Config.https_proxy +ENV['no_proxy']=Chef::Config.no_proxy + +default["openstack"]["developer_mode"] = true + +# The type of token signing to use (uuid or pki) +default["openstack"]["auth"]["strategy"] = "uuid" + +# Set to true where using self-signed certs (in testing environments) +default["openstack"]["auth"]["validate_certs"] = true + +# ========================= Encrypted Databag Setup =========================== +# +# The openstack-common cookbook's default library contains a `secret` +# routine that looks up the value of encrypted databag values. This routine +# uses the secret key file located at the following location to decrypt the +# values in the data bag. +default["openstack"]["secret"]["key_path"] = "/etc/chef/openstack_data_bag_secret" + +# The name of the encrypted data bag that stores service user passwords, with +# each key in the data bag corresponding to a named OpenStack service, like +# "nova", "cinder", etc. +default["openstack"]["secret"]["service_passwords_data_bag"] = "service_passwords" + +# The name of the encrypted data bag that stores DB passwords, with +# each key in the data bag corresponding to a named OpenStack database, like +# "nova", "cinder", etc. +default["openstack"]["secret"]["db_passwords_data_bag"] = "db_passwords" + +# The name of the encrypted data bag that stores Keystone user passwords, with +# each key in the data bag corresponding to a user (Keystone or otherwise). +default["openstack"]["secret"]["user_passwords_data_bag"] = "user_passwords" + +# ========================= Package and Repository Setup ====================== +# +# Various Linux distributions provide OpenStack packages and repositories. +# The provide some sensible defaults, but feel free to override per your +# needs. + +# The coordinated release of OpenStack codename +default["openstack"]["release"] = "grizzly" + +# The Ubuntu Cloud Archive has packages for multiple Ubuntu releases. For +# more information, see: https://wiki.ubuntu.com/ServerTeam/CloudArchive. +# In the component strings, %codename% will be replaced by the value of +# the node["lsb"]["codename"] Ohai value and %release% will be replaced +# by the value of node["openstack"]["release"] +default["openstack"]["apt"]["uri"] = "http://ubuntu-cloud.archive.canonical.com/ubuntu" +default["openstack"]["apt"]["components"] = [ "precise-updates/grizzly", "main" ] +# For the SRU packaging, use this: +# default["openstack"]["apt"]["components"] = [ "%codename%-proposed/%release%", "main" ] + +default["openstack"]["zypp"]["repo-key"] = "05F4861F" # 32 bit key ID +default["openstack"]["zypp"]["uri"] = "http://download.opensuse.org/repositories/Cloud:/OpenStack:/%release%/%suse-release%/" + +#TODO(jaypipes): Do RHEL/Fedora platform family YUM setup +# EPEL repo +default["openstack"]["yum"]["epel"]["url"] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-#{node['platform_version'].to_i}&arch=$basearch" + +if node['platform_version'].to_i >= 6 + set["openstack"]["yum"]["epel"]["key"] = "RPM-GPG-KEY-EPEL-6" +else + set["openstack"]["yum"]["epel"]["key"] = "RPM-GPG-KEY-EPEL" +end +default["openstack"]["yum"]["epel"]["key_url"] = "http://download.fedoraproject.org/pub/epel/#{node['openstack']['yum']['epel']['key']}" + +# openstack repo +default["openstack"]["yum"]["openstack"]["url"]="http://repos.fedorapeople.org/repos/openstack/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" + + + +# ======================== OpenStack Endpoints ================================ +# +# OpenStack recipes often need information about the various service +# endpoints in the deployment. For instance, the cookbook that deploys +# the Nova API service will need to set the glance_api_servers configuration +# option in the nova.conf, and the cookbook setting up the Glance image +# service might need information on the Swift proxy endpoint, etc. Having +# all of this related OpenStack endpoint information in a single set of +# common attributes in the openstack-common cookbook attributes means that +# instead of doing funky role-based lookups, a deployment zone's OpenStack +# endpoint information can simply be accessed by having the +# openstack-common::default recipe added to some base role definition file +# that all OpenStack nodes add to their run list. +# +# node['openstack']['endpoints'] is a hash of hashes, where each value hash +# contains one of more of the following keys: +# +# - scheme +# - uri +# - host +# - port +# - path +# +# If the uri key is set, its value is used as the full URI for the endpoint. +# If the uri key is not set, the endpoint's full URI is constructed from the +# component parts. This allows setups that use some standardized DNS names for +# OpenStack service endpoints in a deployment zone as well as setups that +# instead assign IP addresses (for an actual node or a load balanced virtual +# IP) in a network to a particular OpenStack service endpoint. + +# ******************** OpenStack Identity Endpoints *************************** + +# The OpenStack Identity (Keystone) API endpoint. This is commonly called +# the Keystone Service endpoint... +default['openstack']['endpoints']['identity-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['identity-api']['scheme'] = "http" +default['openstack']['endpoints']['identity-api']['port'] = "5000" +default['openstack']['endpoints']['identity-api']['path'] = "/v2.0" + +# The OpenStack Identity (Keystone) Admin API endpoint +default['openstack']['endpoints']['identity-admin']['host'] = "127.0.0.1" +default['openstack']['endpoints']['identity-admin']['scheme'] = "http" +default['openstack']['endpoints']['identity-admin']['port'] = "35357" +default['openstack']['endpoints']['identity-admin']['path'] = "/v2.0" + +# ****************** OpenStack Compute Endpoints ****************************** + +# The OpenStack Compute (Nova) Native API endpoint +default['openstack']['endpoints']['compute-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['compute-api']['scheme'] = "http" +default['openstack']['endpoints']['compute-api']['port'] = "8774" +default['openstack']['endpoints']['compute-api']['path'] = "/v2/%(tenant_id)s" + +# The OpenStack Compute (Nova) EC2 API endpoint +default['openstack']['endpoints']['compute-ec2-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['compute-ec2-api']['scheme'] = "http" +default['openstack']['endpoints']['compute-ec2-api']['port'] = "8773" +default['openstack']['endpoints']['compute-ec2-api']['path'] = "/services/Cloud" + +# The OpenStack Compute (Nova) EC2 Admin API endpoint +default['openstack']['endpoints']['compute-ec2-admin']['host'] = "127.0.0.1" +default['openstack']['endpoints']['compute-ec2-admin']['scheme'] = "http" +default['openstack']['endpoints']['compute-ec2-admin']['port'] = "8773" +default['openstack']['endpoints']['compute-ec2-admin']['path'] = "/services/Admin" + +# The OpenStack Compute (Nova) XVPvnc endpoint +default['openstack']['endpoints']['compute-xvpvnc']['host'] = "127.0.0.1" +default['openstack']['endpoints']['compute-xvpvnc']['scheme'] = "http" +default['openstack']['endpoints']['compute-xvpvnc']['port'] = "6081" +default['openstack']['endpoints']['compute-xvpvnc']['path'] = "/console" + +# The OpenStack Compute (Nova) novnc endpoint +default['openstack']['endpoints']['compute-novnc']['host'] = "127.0.0.1" +default['openstack']['endpoints']['compute-novnc']['scheme'] = "http" +default['openstack']['endpoints']['compute-novnc']['port'] = "6080" +default['openstack']['endpoints']['compute-novnc']['path'] = "/vnc_auto.html" + +# ******************** OpenStack Network Endpoints **************************** + +# The OpenStack Network (Quantum) API endpoint. +default['openstack']['endpoints']['network-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['network-api']['scheme'] = "http" +default['openstack']['endpoints']['network-api']['port'] = "9696" +# quantumclient appends the protocol version to the endpoint URL, so the +# path needs to be empty +default['openstack']['endpoints']['network-api']['path'] = "" + +# ******************** OpenStack Image Endpoints ****************************** + +# The OpenStack Image (Glance) API endpoint +default['openstack']['endpoints']['image-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['image-api']['scheme'] = "http" +default['openstack']['endpoints']['image-api']['port'] = "9292" +default['openstack']['endpoints']['image-api']['path'] = "/v2" + +# The OpenStack Image (Glance) Registry API endpoint +default['openstack']['endpoints']['image-registry']['host'] = "127.0.0.1" +default['openstack']['endpoints']['image-registry']['scheme'] = "http" +default['openstack']['endpoints']['image-registry']['port'] = "9191" +default['openstack']['endpoints']['image-registry']['path'] = "/v2" + +# ******************** OpenStack Volume Endpoints ***************************** + +# The OpenStack Volume (Cinder) API endpoint +default['openstack']['endpoints']['volume-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['volume-api']['scheme'] = "http" +default['openstack']['endpoints']['volume-api']['port'] = "8776" +default['openstack']['endpoints']['volume-api']['path'] = "/v1/%(tenant_id)s" + +# ******************** OpenStack Metering Endpoints *************************** + +# The OpenStack Metering (Ceilometer) API endpoint +default['openstack']['endpoints']['metering-api']['host'] = "127.0.0.1" +default['openstack']['endpoints']['metering-api']['scheme'] = "http" +default['openstack']['endpoints']['metering-api']['port'] = "8777" +default['openstack']['endpoints']['metering-api']['path'] = "/v1" + +# Alternately, if you used some standardized DNS naming scheme, you could +# do something like this, which would override any part-wise specifications above. +# +# default['openstack']['endpoints']['identity-api']['uri'] = "https://identity.example.com:35357/v2.0" +# default['openstack']['endpoints']['identity-admin']['uri'] = "https://identity.example.com:5000/v2.0" +# default['openstack']['endpoints']['compute-api']['uri'] = "https://compute.example.com:8774/v2/%(tenant_id)s" +# default['openstack']['endpoints']['compute-ec2-api']['uri'] = "https://ec2.example.com:8773/services/Cloud" +# default['openstack']['endpoints']['compute-ec2-admin']['uri'] = "https://ec2.example.com:8773/services/Admin" +# default['openstack']['endpoints']['compute-xvpvnc']['uri'] = "https://xvpvnc.example.com:6081/console" +# default['openstack']['endpoints']['compute-novnc']['uri'] = "https://novnc.example.com:6080/vnc_auto.html" +# default['openstack']['endpoints']['image-api']['uri'] = "https://image.example.com:9292/v2" +# default['openstack']['endpoints']['image-registry']['uri'] = "https://image.example.com:9191/v2" +# default['openstack']['endpoints']['volume-api']['uri'] = "https://volume.example.com:8776/v1/%(tenant_id)s" +# default['openstack']['endpoints']['metering-api']['uri'] = "https://metering.example.com:9000/v1" + +# ======================== OpenStack DB Support ================================ +# +# This section of node attributes stores information about the database hosts +# used in an OpenStack deployment. +# +# There is no 'scheme' key. Instead, there is a 'db_type' key that should +# contain one of 'sqlite', 'mysql', or 'postgresql' +# +# The ::Openstack::db() library routine allows a lookup from any recipe +# to this array, returning the host information for the server that contains +# the database for , where is one of 'compute' (Nova), +# 'image' (Glance), 'identity' (Keystone), 'network' (Quantum), or 'volume' (Cinder) +# +# The ::Openstack::db_connection(, , ) library routine +# returns the SQLAlchemy DB URI for , with the supplied user and password +# that a calling service might be using when connecting to the database. +# +# For example, let's assume that the database that is used by the OpenStack Identity +# service (Keystone) is configured as follows: +# +# host: 192.168.0.3 +# port: 3306 +# db_type: mysql +# db_name: keystone +# +# Further suppose that a node running the OpenStack Identity API service needs to +# connect to the above identity database server. It has the following in it's node +# attributes: +# +# node['db']['user'] = 'keystone' +# +# In a "keystone" recipe, you might find the following code: +# +# user = node['db']['user'] +# pass = secret 'passwords', 'keystone' +# +# sql_connection = ::Openstack::db_uri('identity', user, pass) +# +# The sql_connection variable would then be set to "mysql://keystone:password@192.168.0.3:keystone" +# and could then be written to the keystone.conf file in a template. + +# Default database attributes +default['openstack']['db']['server_role'] = "os-ops-database" +default['openstack']['db']['service_type'] = "mysql" +default['openstack']['db']['port'] = "3306" + +# tenant and user +default['openstack']['identity']['admin_token'] = "openstack_identity_bootstrap_token" + +default['openstack']['identity']['admin_tenant_name'] = "admin" +default['openstack']['identity']['admin_user'] = "admin" +default['openstack']['identity']['admin_password'] = "admin" +#default['openstack']['identity']['roles']['admin'] = "admin" +#default['openstack']['identity']['roles']['member'] = "Member" + +# define enable services +default['openstack']['services'] = { + "compute" => { + "name" => "nova", + "status" => "enable" + }, + "network" => { + "name" => "quantum", + "status" => "enable" + }, + "volume" => { + "name" => "cinder", + "status" => "enable" + }, + "identity" => { + "name" => "keystone", + "status" => "enable" + }, + "image" => { + "name" => "glance", + "status" => "enable" + }, + "object-store" => { + "name" => "swift", + "status" => "disable" + } + } + +# define service users at keystone +default['openstack']['identity']['compute']['username'] = "nova" +default['openstack']['identity']['compute']['password'] = "admin" +default['openstack']['identity']['compute']['role'] = default['openstack']['identity']['roles']['admin'] + +default['openstack']['identity']['network']['username'] = "quantum" +default['openstack']['identity']['network']['password'] = "admin" +default['openstack']['identity']['network']['role'] = default['openstack']['identity']['roles']['admin'] + +default['openstack']['identity']['volume']['username'] = "cinder" +default['openstack']['identity']['volume']['password'] = "admin" +default['openstack']['identity']['volume']['role'] = default['openstack']['identity']['roles']['admin'] + +default['openstack']['identity']['image']['username'] = "glance" +default['openstack']['identity']['image']['password'] = "admin" +default['openstack']['identity']['image']['role'] = default['openstack']['identity']['roles']['admin'] + +default['openstack']['identity']['objectstorage']['username'] = "swift" +default['openstack']['identity']['objectstorage']['password'] = "admin" +default['openstack']['identity']['objectstorage']['role'] = default['openstack']['identity']['roles']['admin'] + + +# Database used by the OpenStack Compute (Nova) service +default['openstack']['db']['compute']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['compute']['host'] = "127.0.0.1" +default['openstack']['db']['compute']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['compute']['db_name'] = "nova" +default['openstack']['db']['compute']['username'] = "nova" +default['openstack']['db']['compute']['password'] = "admin" + + +# Database used by the OpenStack Identity (Keystone) service +default['openstack']['db']['identity']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['identity']['host'] = "127.0.0.1" +default['openstack']['db']['identity']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['identity']['db_name'] = "keystone" +default['openstack']['db']['identity']['username'] = "keystone" +default['openstack']['db']['identity']['password'] = "admin" + + +# Database used by the OpenStack Image (Glance) service +default['openstack']['db']['image']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['image']['host'] = "127.0.0.1" +default['openstack']['db']['image']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['image']['db_name'] = "glance" +default['openstack']['db']['image']['username'] = "glance" +default['openstack']['db']['image']['password'] = "admin" + +# Database used by the OpenStack Network (Quantum) service +default['openstack']['db']['network']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['network']['host'] = "127.0.0.1" +default['openstack']['db']['network']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['network']['db_name'] = "quantum" +default['openstack']['db']['network']['username'] = "quantum" +default['openstack']['db']['network']['password'] = "admin" + +# Database used by the OpenStack Volume (Cinder) service +default['openstack']['db']['volume']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['volume']['host'] = "127.0.0.1" +default['openstack']['db']['volume']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['volume']['db_name'] = "cinder" +default['openstack']['db']['volume']['username'] = "cinder" +default['openstack']['db']['volume']['password'] = "admin" + +# Database used by the OpenStack Dashboard (Horizon) +default['openstack']['db']['dashboard']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['dashboard']['host'] = "127.0.0.1" +default['openstack']['db']['dashboard']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['dashboard']['db_name'] = "horizon" +default['openstack']['db']['dashboard']['username'] = "horizon" +default['openstack']['db']['dashboard']['password'] = "admin" + +# Database used by OpenStack Metering (Ceilometer) +default['openstack']['db']['metering']['db_type'] = node['openstack']['db']['service_type'] +default['openstack']['db']['metering']['host'] = "127.0.0.1" +default['openstack']['db']['metering']['port'] = node['openstack']['db']['port'] +default['openstack']['db']['metering']['db_name'] = "ceilometer" +default['openstack']['db']['metering']['username'] = "ceilometer" +default['openstack']['db']['metering']['password'] = "admin" + + +# Switch to store the MySQL root password in a databag instead of +# using the generated OpenSSL cookbook secure_password one. +default['openstack']['db']['root_user_use_databag'] = false + +# If above root_user_use_databag is true, the below string +# will be passed to the user_password library routine. +default['openstack']['db']['root_user_key'] = 'mysqlroot' + +# logging.conf list keypairs module_name => log level to write +default['openstack']['logging']['ignore'] = {'nova.api.openstack.wsgi' => 'WARNING', + 'nova.osapi_compute.wsgi.server' => 'WARNING'} + +default['openstack']['memcached_servers'] = nil + +# Default database attributes +default["openstack"]["mq"]["server_role"] = "os-ops-messaging" +default["openstack"]["mq"]["service_type"] = "rabbitmq" +default["openstack"]["mq"]["bind_address"] = "0.0.0.0" +default["openstack"]["mq"]["port"] = "5672" +default["openstack"]["mq"]["user"] = "guest" +default["openstack"]["mq"]["vhost"] = "/" diff --git a/chef/cookbooks/openstack-common/libraries/database.rb b/chef/cookbooks/openstack-common/libraries/database.rb new file mode 100644 index 0000000..089d0f7 --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/database.rb @@ -0,0 +1,102 @@ +# +# Cookbook Name:: openstack-common +# library:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ::Openstack + # Library routine that uses the database cookbook to create the + # service's database and grant read/write access to the + # given user and password. + # + # A privileged "super user" and password is determined from the + # underlying database cookbooks. For instance, if a MySQL database + # is used, the node["mysql"]["server_root_password"] is used along + # with the "root" (super)user. + def db_create_with_user service, user, pass + root_user_use_databag = node['openstack']['db']['root_user_use_databag'] + info = db service + if info + host = info['host'] + port = info['port'].to_s + type = info['db_type'] + db_name = info['db_name'] + case type + when "postgresql", "pgsql" + include_recipe "database::postgresql" + db_prov = ::Chef::Provider::Database::Postgresql + user_prov = ::Chef::Provider::Database::PostgresqlUser + super_user = "postgres" + if root_user_use_databag + user_key = node['openstack']['db']['root_user_key'] + super_password = user_password user_key + else + super_password = node['postgresql']['password']['postgres'] + end + when "mysql" + # we have to install the 'mysql' gem, otherwise the provider won't work + include_recipe "database::mysql" + db_prov = ::Chef::Provider::Database::Mysql + user_prov = ::Chef::Provider::Database::MysqlUser + super_user = "root" + + if root_user_use_databag + user_key = node['openstack']['db']['root_user_key'] + super_password = user_password user_key + else + super_password = node['mysql']['server_root_password'] + end + else + ::Chef::Log.error("Unsupported database type #{type}") + end + + connection_info = { + :host => host, + :port => port.to_i, + :username => super_user, + :password => super_password + } + + # create database + database "create #{db_name} database" do + provider db_prov + connection connection_info + database_name db_name + action :create + end + + # create user + database_user user do + provider user_prov + connection connection_info + password pass + action :create + end + + # grant privs to user + database_user user do + provider user_prov + connection connection_info + password pass + database_name db_name + host '%' + privileges [:all] + action :grant + end + end + info + end +end diff --git a/chef/cookbooks/openstack-common/libraries/endpoints.rb b/chef/cookbooks/openstack-common/libraries/endpoints.rb new file mode 100644 index 0000000..92812fa --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/endpoints.rb @@ -0,0 +1,86 @@ +# +# Cookbook Name:: openstack-common +# library:: endpoints +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +module ::Openstack + # Shortcut to get the full URI for an endpoint. If the "uri" key isn't + # set in the endpoint hash, we use the ::Openstack.get_uri_from_mash + # library routine from the openstack-common cookbook to grab a URI object + # and construct the URI object from the endpoint parts. + def endpoint name + ep = endpoint_for name + if ep && ep['uri'] + ::URI.parse ::URI.encode(ep['uri']) + elsif ep + uri_from_hash ep + end + end + + # Useful for iterating over the OpenStack endpoints + def endpoints &block + node['openstack']['endpoints'].each do | name, info | + block.call(name, info) + end + rescue + nil + end + + # Instead of specifying the verbose node["openstack"]["db"][service], + # this shortcut allows the simpler and shorter db(service), where + # service is one of 'compute', 'image', 'identity', 'network', + # and 'volume' + def db service + node['openstack']['db'][service] + rescue + nil + end + + # Shortcut to get the SQLAlchemy DB URI for a named service + def db_uri service, user, pass + info = db(service) + if info + host = info['host'] + port = info['port'].to_s + type = info['db_type'] + name = info['db_name'] + if type == "pgsql" + # Normalize to the SQLAlchemy standard db type identifier + type = "postgresql" + end + case type + when "mysql", "postgresql" + result = "#{type}://#{user}:#{pass}@#{host}:#{port}/#{name}" + when "sqlite" + # SQLite uses filepaths not db name + path = info['path'] + result = "sqlite://#{path}" + end + end + end + +private + # Instead of specifying the verbose node["openstack"]["endpoints"][name], + # this shortcut allows the simpler and shorter endpoint(name) + def endpoint_for name + node['openstack']['endpoints'][name] + rescue + nil + end +end diff --git a/chef/cookbooks/openstack-common/libraries/network.rb b/chef/cookbooks/openstack-common/libraries/network.rb new file mode 100644 index 0000000..fca7e28 --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/network.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: openstack-common +# library:: address +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ::Openstack + # return the IPv4 (default) address of the given interface. + # + # @param [String] interface The interface to query. + # @param [String] family The protocol family to use. + # @return [String] The IPv4 address. + def address_for interface, family="inet" + interface_node = node["network"]["interfaces"][interface]["addresses"] + interface_node.select do |address, data| + if data['family'] == family + return address + end + end + end +end diff --git a/chef/cookbooks/openstack-common/libraries/parse.rb b/chef/cookbooks/openstack-common/libraries/parse.rb new file mode 100644 index 0000000..50f0ffa --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/parse.rb @@ -0,0 +1,63 @@ +# +# Cookbook Name:: openstack-common +# library:: parse +# +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ::Openstack + + # The current state of (at least some) OpenStack CLI tools do not provide a + # mechanism for outputting data in formats other than PrettyTable output. + # Therefore this function is intended to parse PrettyTable output into a + # usable array of hashes. Similarly, it will flatten Property/Value tables + # into a single element array. + # table - the raw PrettyTable output of the CLI command + # output - array of hashes representing the data. + def prettytable_to_array table + ret = [] + return ret if table == nil + indicies = [] + (table.split(/$/).collect{|x| x.strip}).each { |line| + unless line.start_with?('+--') or line.empty? + cols = line.split('|').collect{|x| x.strip} + cols.shift + if indicies == [] + indicies = cols + next + end + newobj = {} + cols.each { |val| + newobj[indicies[newobj.length]] = val + } + ret.push(newobj) + end + } + + # this kinda sucks, but some prettytable data comes + # as Property Value pairs. If this is the case, then + # flatten it as expected. + newobj = {} + if indicies == ['Property', 'Value'] + ret.each { |x| + newobj[x['Property']] = x['Value'] + } + [newobj] + else + ret + end + end + +end diff --git a/chef/cookbooks/openstack-common/libraries/passwords.rb b/chef/cookbooks/openstack-common/libraries/passwords.rb new file mode 100644 index 0000000..9e58e16 --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/passwords.rb @@ -0,0 +1,71 @@ +# +# Cookbook Name:: openstack-common +# library:: passwords +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ::Openstack + # Library routine that returns an encrypted data bag value + # for a supplied string. The key used in decrypting the + # encrypted value should be located at + # node["openstack"]["secret"]["key_path"]. + # + # Note that if node["openstack"]["developer_mode"] is true, + # then the value of the index parameter is just returned as-is. This + # means that in developer mode, if a cookbook does this: + # + # class Chef + # class Recipe + # include ::Openstack + # end + # end + # + # nova_password = secret "passwords", "nova" + # + # That means nova_password will == "nova". + def secret bag_name, index + if node["openstack"]["developer_mode"] + return index + end + key_path = node["openstack"]["secret"]["key_path"] + ::Chef::Log.info "Loading encrypted databag #{bag_name}.#{index} using key at #{key_path}" + secret = ::Chef::EncryptedDataBagItem.load_secret key_path + ::Chef::EncryptedDataBagItem.load(bag_name, index, secret)[index] + end + + # Ease-of-use/standardization routine that returns a service password + # for a named OpenStack service. Note that databases are named + # after the OpenStack project nickname, like "nova" or "glance" + def service_password service + bag = node["openstack"]["secret"]["service_passwords_data_bag"] + secret bag, service + end + + # Ease-of-use/standardization routine that returns a database password + # for a named OpenStack database. Note that databases are named + # after the OpenStack project nickname, like "nova" or "glance" + def db_password service + bag = node["openstack"]["secret"]["db_passwords_data_bag"] + secret bag, service + end + + # Ease-of-use/standardization routine that returns a password + # for a user. + def user_password user + bag = node["openstack"]["secret"]["user_passwords_data_bag"] + secret bag, user + end +end diff --git a/chef/cookbooks/openstack-common/libraries/search.rb b/chef/cookbooks/openstack-common/libraries/search.rb new file mode 100644 index 0000000..8e97c6e --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/search.rb @@ -0,0 +1,79 @@ +# +# Cookbook Name:: openstack-common +# library:: search +# +# Copyright 2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module ::Openstack + # Search the nodes environment for the given role or recipe. + # + # @param [String] The role or recipe to be found. + # @return [Array] The matching result or an empty list. + def search_for r, &block + role_query = "(chef_environment:#{node.chef_environment} AND roles:#{r})" + recipe_query = "(chef_environment:#{node.chef_environment} AND recipes:#{r})".sub("::","\\:\\:") + query = "#{role_query} OR #{recipe_query}" + + resp = search(:node, query, &block) + resp ? resp : [] + end + + # Returns the value for ["openstack"]["memcached_servers"] when + # set, otherwise will perform a search. + # + # @param [String] role The role to be found (optional). + # @return [Array] A list of memcached servers in format + # ':'. + def memcached_servers role="infra-caching" + unless node['openstack']['memcached_servers'] + search_for(role).map do |n| + listen = n['memcached']['listen'] + port = n['memcached']['port'] || "11211" + + "#{listen}:#{port}" + end.sort + else + node['openstack']['memcached_servers'].length != 0 ? + node['openstack']['memcached_servers'] : [] + end + end + + # Returns all rabbit servers. + # Uses the value for ["openstack"]["mq"]["servers"] when set, otherwise + # will perform a search. + # + # @return [String] Rabbit servers joined by a comma in + # the format of ':'. + def rabbit_servers + if node["openstack"]["mq"]["servers"] + servers = node["openstack"]["mq"]["servers"] + port = node["openstack"]["mq"]["port"] + + servers.map { |s| "#{s}:#{port}" }.join "," + else + role = node["openstack"]["mq"]["server_role"] + search_for(role).map do |n| + # The listen attribute should be saved to the node + # in the wrapper cookbook. See the reference cookbook + # openstack-ops-messaging. + address = n["openstack"]["mq"]["listen"] + port = n["openstack"]["mq"]["port"] + + "#{address}:#{port}" + end.sort.join "," + end + end +end diff --git a/chef/cookbooks/openstack-common/libraries/uri.rb b/chef/cookbooks/openstack-common/libraries/uri.rb new file mode 100644 index 0000000..15d255e --- /dev/null +++ b/chef/cookbooks/openstack-common/libraries/uri.rb @@ -0,0 +1,56 @@ +# +# Cookbook Name:: openstack-common +# library:: uri +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +module ::Openstack + # Returns a uri::URI from a hash. If the hash has a "uri" key, the value + # of that is returned. If not, then the routine attempts to construct + # the URI from other parts of the hash, notably looking for keys of + # "host", "port", "scheme", and "path" to construct the URI. + # + # Returns nil if neither "uri" or "host" keys exist in the supplied + # hash. + def uri_from_hash hash + if hash['uri'] + ::URI.parse hash['uri'] + else + return nil unless hash['host'] + + scheme = hash['scheme'] ? hash['scheme'] : "http" + host = hash['host'] + port = hash['port'] # Returns nil if missing, which is fine. + path = hash['path'] # Returns nil if missing, which is fine. + ::URI::Generic.new scheme, nil, host, port, nil, path, nil, nil, nil + end + end + + # Helper for joining URI paths. The standard URI::join method is not + # intended for joining URI relative path segments. This function merely + # helps to accurately join supplied paths. + def uri_join_paths(*paths) + return nil if paths.length == 0 + leadingslash = paths[0][0] == '/' ? '/' : '' + trailingslash = paths[-1][-1] == '/' ? '/' : '' + paths.map! { |path| + path = path.sub(/^\/+/,'').sub(/\/+$/,'') + } + leadingslash + paths.join('/') + trailingslash + end +end diff --git a/chef/cookbooks/openstack-common/metadata.rb b/chef/cookbooks/openstack-common/metadata.rb new file mode 100644 index 0000000..584ccea --- /dev/null +++ b/chef/cookbooks/openstack-common/metadata.rb @@ -0,0 +1,18 @@ +name "openstack-common" +maintainer "AT&T Services, Inc." +maintainer_email "cookbooks@lists.tfoundry.com" +license "Apache 2.0" +description "Common OpenStack attributes, libraries and recipes." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.4.3" + +recipe "openstack-common", "Installs/Configures common recipes" +recipe "openstack-common::logging", "Installs/Configures common logging" + +%w{ ubuntu suse }.each do |os| + supports os +end + +depends "apt" +depends "yum" +depends "database" diff --git a/chef/cookbooks/openstack-common/recipes/databag.rb b/chef/cookbooks/openstack-common/recipes/databag.rb new file mode 100644 index 0000000..10792f3 --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/databag.rb @@ -0,0 +1,359 @@ +# +# Cookbook Name:: openstack-common +# Attributes:: default +# +# Copyright 2013, Futurewei, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# import initial data from databag with customized user data. +# + +class ::Chef::Recipe + include ::Openstack +end + +defaultbag = "openstack" +if !Chef::DataBag.list.key?(defaultbag) + Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.") + return +end + +myitem = node.attribute?('cluster')? node['cluster']:"env_default" + +if !search(defaultbag, "id:#{myitem}") + Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.") + return +end + +mydata = data_bag_item(defaultbag, myitem) +# use unsecreted text username and password at chef server. +node.override['openstack']['developer_mode'] = mydata['credential']['text'] + +# The coordinated release of OpenStack codename +node.override['openstack']['release'] = mydata['release'] + +# Openstack repo setup +# ubuntu +node.override['openstack']['apt']['components'] = [ "precise-updates/#{node['openstack']['release']}", "main" ] + +# redhat +node.override['openstack']['yum']['openstack']['url']="http://repos.fedorapeople.org/repos/openstack/openstack-#{node['openstack']['release']}/epel-#{node['platform_version'].to_i}/" + +# Tenant and user +node.override['openstack']['identity']['admin_token'] = mydata['credential']['identity']['token']['admin'] +node.override['openstack']['identity']['tenants'] = ["#{ mydata['credential']['identity']['tenants']['admin']}", "#{ mydata['credential']['identity']['tenants']['service']}"] +node.override["openstack"]["identity"]["roles"] = ["#{ mydata['credential']['identity']['roles']['admin']}", "#{ mydata['credential']['identity']['roles']['member']}"] + +node.override['openstack']['identity']['admin_tenant_name'] = mydata['credential']['identity']['tenants']['admin'] +node.override['openstack']['identity']['admin_user'] = mydata['credential']['identity']['users']['admin']['username'] +node.override['openstack']['identity']['admin_password'] = mydata['credential']['identity']['users']['admin']['password'] + +# services with related usernames and passwords +node['openstack']['services'].each_key do |service| + node.override['openstack']['services']["#{service}"]['name'] = mydata['services']["#{service}"]['name'] + node.override['openstack']['services']["#{service}"]['status'] = mydata['services']["#{service}"]['status'] + + if service != "object-store" + node.set['openstack']['db']["#{service}"]['username'] = mydata['credential']['mysql']["#{service}"]['username'] + node.set['openstack']['db']["#{service}"]['password'] = mydata['credential']['mysql']["#{service}"]['password'] + end + + if "#{service}" != "identity" and "#{service}" != "dashboard" + node.override['openstack']['identity']["#{service}"]['username'] = mydata['credential']['identity']['users']["#{service}"]['username'] + node.override['openstack']['identity']["#{service}"]['password'] = mydata['credential']['identity']['users']["#{service}"]['password'] + node.override['openstack']['identity']["#{service}"]['role'] = mydata['credential']['identity']['roles']['admin'] + node.set['openstack']['identity']["#{service}"]['tenant'] = mydata['credential']['identity']['users']["#{service}"]['tenant'] + end +end + + +# network plugins +node.override["openstack"]["network"]["plugins"] = ['openvswitch', 'openvswitch-agent'] + + + +# ======================== OpenStack Endpoints ================================ +# Identity (keystone) +node.override['openstack']['endpoints']['identity-api']['host'] = mydata['endpoints']['identity']['service']['host'] +node.override['openstack']['endpoints']['identity-api']['scheme'] = mydata['endpoints']['identity']['service']['scheme'] +node.override['openstack']['endpoints']['identity-api']['port'] = "5000" +node.override['openstack']['endpoints']['identity-api']['path'] = "/v2.0" + +node.override['openstack']['endpoints']['identity-admin']['host'] = mydata['endpoints']['identity']['admin']['host'] +node.override['openstack']['endpoints']['identity-admin']['scheme'] = mydata['endpoints']['identity']['admin']['scheme'] +node.override['openstack']['endpoints']['identity-admin']['port'] = "35357" +node.override['openstack']['endpoints']['identity-admin']['path'] = "/v2.0" + +# Compute (Nova) +node.override['openstack']['endpoints']['compute-api']['host'] = mydata['endpoints']['compute']['service']['host'] +node.override['openstack']['endpoints']['compute-api']['scheme'] = mydata['endpoints']['compute']['service']['scheme'] +#node.override['openstack']['endpoints']['compute-api']['port'] = "8774" +#node.override['openstack']['endpoints']['compute-api']['path'] = "/v2/%(tenant_id)s" + +# The OpenStack Compute (Nova) EC2 API endpoint +node.override['openstack']['endpoints']['compute-ec2-api']['host'] = mydata['endpoints']['ec2']['service']['host'] +node.override['openstack']['endpoints']['compute-ec2-api']['scheme'] = mydata['endpoints']['ec2']['service']['scheme'] +#node.override['openstack']['endpoints']['compute-ec2-api']['port'] = "8773" +#node.override['openstack']['endpoints']['compute-ec2-api']['path'] = "/services/Cloud" + +# The OpenStack Compute (Nova) EC2 Admin API endpoint +node.override['openstack']['endpoints']['compute-ec2-admin']['host'] = mydata['endpoints']['ec2']['admin']['host'] +node.override['openstack']['endpoints']['compute-ec2-admin']['scheme'] = mydata['endpoints']['ec2']['admin']['scheme'] +#node.override['openstack']['endpoints']['compute-ec2-admin']['port'] = "8773" +#node.override['openstack']['endpoints']['compute-ec2-admin']['path'] = "/services/Admin" + +# The OpenStack Compute (Nova) XVPvnc endpoint +node.override['openstack']['endpoints']['compute-xvpvnc']['host'] = mydata['endpoints']['compute']['xvpvnc']['host'] +node.override['openstack']['endpoints']['compute-xvpvnc']['scheme'] = mydata['endpoints']['compute']['xvpvnc']['scheme'] +#node.override['openstack']['endpoints']['compute-xvpvnc']['port'] = "6081" +#node.override['openstack']['endpoints']['compute-xvpvnc']['path'] = "/console" + +# The OpenStack Compute (Nova) novnc endpoint +node.override['openstack']['endpoints']['compute-novnc']['host'] = mydata['endpoints']['compute']['novnc']['host'] +node.override['openstack']['endpoints']['compute-novnc']['scheme'] = mydata['endpoints']['compute']['novnc']['scheme'] +#node.override['openstack']['endpoints']['compute-novnc']['port'] = "6080" +#node.override['openstack']['endpoints']['compute-novnc']['path'] = "/vnc_auto.html" + + +# Network (Quantum) +node.override['openstack']['endpoints']['network-api']['host'] = mydata['endpoints']['network']['service']['host'] +node.override['openstack']['endpoints']['network-api']['scheme'] = mydata['endpoints']['network']['service']['scheme'] +#node.override['openstack']['endpoints']['network-api']['port'] = "9696" +# quantumclient appends the protocol version to the endpoint URL, so the + +# Image (Glance) +node.override['openstack']['endpoints']['image-api']['host'] = mydata['endpoints']['image']['service']['host'] +node.override['openstack']['endpoints']['image-api']['scheme'] = mydata['endpoints']['image']['service']['scheme'] +#node.override['openstack']['endpoints']['image-api']['port'] = "9292" +#node.override['openstack']['endpoints']['image-api']['path'] = "/v2" + +# Image (Glance) Registry +node.override['openstack']['endpoints']['image-registry']['host'] = mydata['endpoints']['image']['registry']['host'] +node.override['openstack']['endpoints']['image-registry']['scheme'] = mydata['endpoints']['image']['registry']['scheme'] +node.override['openstack']['endpoints']['image-registry']['port'] = "9191" +node.override['openstack']['endpoints']['image-registry']['path'] = "/v2" + +# Volume (Cinder) +node.override['openstack']['endpoints']['volume-api']['host'] = mydata['endpoints']['volume']['service']['host'] +node.override['openstack']['endpoints']['volume-api']['scheme'] = mydata['endpoints']['volume']['service']['scheme'] +#node.override['openstack']['endpoints']['volume-api']['port'] = "8776" +#node.override['openstack']['endpoints']['volume-api']['path'] = "/v1/%(tenant_id)s" + +# Metering (Ceilometer) +node.override['openstack']['endpoints']['metering-api']['host'] = mydata['endpoints']['metering']['service']['host'] +node.override['openstack']['endpoints']['metering-api']['scheme'] = mydata['endpoints']['metering']['service']['scheme'] +#node.override['openstack']['endpoints']['metering-api']['port'] = "8777" +#node.override['openstack']['endpoints']['metering-api']['path'] = "/v1" + + +# ======================== OpenStack DB Support ================================ +# set database attributes +#node.override['openstack']['db']['server_role'] = "os-ops-database" +node.override['openstack']['db']['service_type'] = mydata['db']['service_type'] +node.override['openstack']['db']['bind_address'] = mydata['db']["#{node['openstack']['db']['service_type']}"]['bind_address'] +node.override['openstack']['db']['port'] = mydata['db']["#{node['openstack']['db']['service_type']}"]['port'] + +node.override['openstack']['db']['super']['username'] = mydata['credential']["#{node['openstack']['db']['service_type']}"]['super']['username'] +node.override['openstack']['db']['super']['password'] = mydata['credential']["#{node['openstack']['db']['service_type']}"]['super']['password'] + +# Database used by the OpenStack Compute (Nova) service +node.override['openstack']['db']['compute']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['compute']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['compute']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['compute']['db_name'] = "nova" + +# Database used by the OpenStack Identity (Keystone) service +node.override['openstack']['db']['identity']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['identity']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['identity']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['identity']['db_name'] = "keystone" + +# Database used by the OpenStack Image (Glance) service +node.override['openstack']['db']['image']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['image']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['image']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['image']['db_name'] = "glance" + +# Database used by the OpenStack Network (Quantum) service +node.override['openstack']['db']['network']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['network']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['network']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['network']['db_name'] = "quantum" + +# Database used by the OpenStack Volume (Cinder) service +node.override['openstack']['db']['volume']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['volume']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['volume']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['volume']['db_name'] = "cinder" + +# Database used by the OpenStack Dashboard (Horizon) +node.override['openstack']['db']['dashboard']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['dashboard']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['dashboard']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['dashboard']['db_name'] = "horizon" + +# Database used by OpenStack Metering (Ceilometer) +node.override['openstack']['db']['metering']['db_type'] = node['openstack']['db']['service_type'] +node.override['openstack']['db']['metering']['host'] = node['openstack']['db']['bind_address'] +node.override['openstack']['db']['metering']['port'] = node['openstack']['db']['port'] +#node.override['openstack']['db']['metering']['db_name'] = "ceilometer" + +# node.override database attributes +#node.override['openstack']['mq']['server_role'] = "os-ops-messaging" +node.override['openstack']['mq']['service_type'] = mydata['mq']['service_type'] +node.override['openstack']['mq']['bind_address'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['bind_address'] +node.override['openstack']['mq']['port'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['port'] +node.override['openstack']['mq']['user'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username'] +node.override['openstack']['mq']['password'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password'] +#node.override['openstack']['mq']['vhost'] = "/" + + + + +# ============================= Network service Configuration ===================== +# Gets set in the Network Endpoint when registering with Keystone +node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] +node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] +node.override["openstack"]["network"]["service_name"] = node['openstack']['services']['network']['name'] +# node.override["openstack"]["network"]["service_type"] = "network" +# node.override["openstack"]["network"]["description"] = "OpenStack Networking service" + +# The rabbit user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +#node.override["openstack"]["network"]["rabbit_server_chef_role"] = "rabbitmq-server" +node.override["openstack"]["network"]["rabbit"]["username"] = node['openstack']['mq']['user'] +#node.override["openstack"]["network"]["rabbit"]["vhost"] = "/" +node.override["openstack"]["network"]["rabbit"]["port"] = node['openstack']['mq']['port'] +node.override["openstack"]["network"]["rabbit"]["host"] = node['openstack']['mq']['bind_address'] +#node.override["openstack"]["network"]["rabbit"]["ha"] = false + +# The database username for the quantum database +node.override["openstack"]["network"]["db"]["username"] = mydata['credential']['mysql']['network']['username'] + +# Used in the Keystone authtoken middleware configuration +node.override["openstack"]["network"]["service_tenant_name"] = node['openstack']['identity']['network']['tenant'] +node.override["openstack"]["network"]["service_user"] = node['openstack']['identity']['network']['username'] +node.override["openstack"]["network"]["service_role"] = node['openstack']['identity']['network']['role'] + +node.override["openstack"]["network"]["api"]["bind_interface"] = mydata['networking']['control']['interface'] + +node.set['openstack']['networking']['control']['interface'] = mydata['networking']['control']['interface'] +node.set['openstack']['networking']['tenant']['interface'] = mydata['networking']['tenant']['interface'] +node.set['openstack']['networking']['public']['interface'] = mydata['networking']['public']['interface'] +node.set['openstack']['networking']['storage']['interface'] = mydata['networking']['storage']['interface'] + + +# ============================= L3 Agent Configuration ===================== + +# Indicates that this L3 agent should also handle routers that do not have +# an external network gateway configured. This option should be True only +# for a single agent in a Quantum deployment, and may be False for all agents +# if all routers must have an external network gateway +#node.override["openstack"]["network"]["l3"]["handle_internal_only_routers"] = "False" + +# Name of bridge used for external network traffic. This should be set to +# empty value for the linux bridge +# node.override["openstack"]["network"]["l3"]["external_network_bridge"] = "br-ex" + +# Interface to use for external bridge. +node.override["openstack"]["network"]["l3"]["external_network_bridge_interface"] = mydata['networking']['public']['interface'] + + +# ============================= Metadata Agent Configuration =============== + +# The location of the Nova Metadata API service to proxy to (nil uses node.override) +node.override["openstack"]["network"]["metadata"]["nova_metadata_ip"] = mydata['endpoints']['compute']['service']['host'] +#node.override["openstack"]["network"]["metadata"]["nova_metadata_port"] = 8775 + +# The name of the secret databag containing the metadata secret +node.override["openstack"]["network"]["metadata"]["secret_name"] = mydata['credential']['metadata']['password'] + + +# ============================= OVS Plugin Configuration =================== + +# Type of network to allocate for tenant networks. The node.override value 'local' is +# useful only for single-box testing and provides no connectivity between hosts. +# You MUST either change this to 'vlan' and configure network_vlan_ranges below +# or change this to 'gre' and configure tunnel_id_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to disable creation +# of tenant networks. + +tenant_network_type = mydata['networking']['plugins']['ovs']['tenant_network_type'] +node.override["openstack"]["network"]["openvswitch"]["tenant_network_type"] =tenant_network_type + +# Comma-separated list of [::] tuples enumerating +# ranges of VLAN IDs on named physical networks that are available for allocation. +# All physical networks listed are available for flat and VLAN provider network +# creation. Specified ranges of VLAN IDs are available for tenant network +# allocation if tenant_network_type is 'vlan'. If empty, only gre and local +# networks may be created +# +# Example: network_vlan_ranges = physnet1:1000:2999 +node.override["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] =mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['network_vlan_ranges'] + +# Set to True in the server and the agents to enable support +# for GRE networks. Requires kernel support for OVS patch ports and +# GRE tunneling. +node.override["openstack"]["network"]["openvswitch"]["enable_tunneling"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['enable_tunneling'] + +# Comma-separated list of : tuples +# enumerating ranges of GRE tunnel IDs that are available for tenant +# network allocation if tenant_network_type is 'gre'. +# +# Example: tunnel_id_ranges = 1:1000 +node.override["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_id_ranges'] + +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch bay". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity +node.override["openstack"]["network"]["openvswitch"]["integration_bridge"] = mydata['networking']['plugins']['ovs']['integration_bridge'] + +# Only used for the agent if tunnel_id_ranges (above) is not empty for +# the server. In most cases, the node.override value should be fine +node.override["openstack"]["network"]["openvswitch"]["tunnel_bridge"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['tunnel_bridge'] + +# Uncomment this line for the agent if tunnel_id_ranges (above) is not +# empty for the server. Set local_ip to be the local IP address of +# this hypervisor or set the local_ip_interface parameter to use the IP +# address of the specified interface. If local_ip_interface is set +# it will take precedence. +local_ip_interface = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['local_ip_interface'] +if local_ip_interface != ("nil") + local_ip= address_for(local_ip_interface) +else + local_ip="nil" +end + +node.override["openstack"]["network"]["openvswitch"]["local_ip"] = local_ip +node.override["openstack"]["network"]["openvswitch"]["local_ip_interface"] = local_ip_interface + +# Comma-separated list of : tuples +# mapping physical network names to the agent's node-specific OVS +# bridge names to be used for flat and VLAN networks. The length of +# bridge names should be no more than 11. Each bridge must +# exist, and should have a physical network interface configured as a +# port. All physical networks listed in network_vlan_ranges on the +# server should have mappings to appropriate bridges on each agent. +# +# Example: bridge_mappings = physnet1:br-eth1 +node.override["openstack"]["network"]["openvswitch"]["bridge_mappings"] = mydata['networking']['plugins']['ovs']["#{tenant_network_type}"]['bridge_mappings'] + + +# #### nova ##### +node.override["openstack"]["compute"]["network"]["service_type"] = mydata['networking']['nova']['network_type'] + diff --git a/chef/cookbooks/openstack-common/recipes/default.rb b/chef/cookbooks/openstack-common/recipes/default.rb new file mode 100644 index 0000000..dc018cd --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/default.rb @@ -0,0 +1,116 @@ +# +# Cookbook Name:: openstack-common +# library:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-common::databag" + +case node["platform_family"] +when "debian" + package "ubuntu-cloud-keyring" do + action :install + end + + apt_uri = node["openstack"]["apt"]["uri"] + apt_components = node["openstack"]["apt"]["components"] + + # Simple variable substitution for LSB codename and OpenStack release + apt_components.each do | comp | + comp = comp.gsub "%release%", node["openstack"]["release"] + comp = comp.gsub "%codename%", node["lsb"]["codename"] + end + + apt_repository "openstack-ppa" do + uri node["openstack"]["apt"]["uri"] + components apt_components + end + +when "suse" + if node["lsb"]["description"].nil? + # Workaround for SLE11 + # + # On SLE11 ohai is broken and prefers lsb-release. We need to + # install it to be able to detect if recipe is run on openSUSE or SLES. + # + # https://bugzilla.novell.com/show_bug.cgi?id=809129 + # + # + install_lsb_release = package "lsb-release" do + action :nothing + end + reload_ohai = ohai "reload_lsb" do + action :nothing + end + install_lsb_release.run_action(:install) + reload_ohai.run_action(:reload) + end + if node["lsb"]["description"][/^SUSE Linux Enterprise Server/] + release, patchlevel = node["platform_version"].split(".") + zypp_release = "SLE_#{release}_SP#{patchlevel}" + elsif node["lsb"]["description"][/^openSUSE/] + zypp_release = "openSUSE_" + node["lsb"]["release"] + end + zypp = node["openstack"]["zypp"] + repo_uri = zypp["uri"].gsub( + "%release%", node["openstack"]["release"].capitalize) + repo_uri.gsub! "%suse-release%", zypp_release + repo_alias = "Cloud:OpenStack:" + node["openstack"]["release"].capitalize + + # TODO(iartarisi) this should be moved to its own cookbook + bash "add repository key" do + cwd "/tmp" + code <<-EOH + gpg --keyserver pgp.mit.edu --recv-keys #{zypp["repo-key"]} + gpg --armor --export #{zypp["repo-key"]} > cloud.asc + rpm --import cloud.asc + rm -f cloud.asc + EOH + + not_if { `rpm -qa gpg-pubkey*`.include? zypp["repo-key"].downcase } + end + + execute "add repository" do + command "zypper addrepo --check #{repo_uri} #{repo_alias}" + not_if { `zypper repos --export -`.include? repo_uri } + end + +when "rhel", "fedora" + if node['platform_version'].to_f < 6.4 + Chef::Log.error("The client(IP: #{node['ipaddress']}) OS #{node['platform']} #{node['platform_version']} is lower than 6.4") + return + end + + # add epel repo + include_recipe "yum::epel" + + # add repoforge repo + # include_recipe "yum::repoforge" + + yum_repository "openstack" do + description "redhat packages for openstack" + url node["openstack"]["yum"]["openstack"]["url"] + # mirrorlist true + action :create + end + + execute "yum-update" do + user "root" + command "yum -y update" + action :run + end +end + diff --git a/chef/cookbooks/openstack-common/recipes/logging.rb b/chef/cookbooks/openstack-common/recipes/logging.rb new file mode 100644 index 0000000..877ff5f --- /dev/null +++ b/chef/cookbooks/openstack-common/recipes/logging.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: openstack-common +# library:: logging +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +directory "/etc/openstack" do + owner "root" + group "root" + mode 00755 + action :create +end + +template "/etc/openstack/logging.conf" do + source "logging.conf.erb" + owner "root" + group "root" + mode 00644 +end diff --git a/chef/cookbooks/openstack-common/spec/database_spec.rb b/chef/cookbooks/openstack-common/spec/database_spec.rb new file mode 100644 index 0000000..5b3f0fa --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/database_spec.rb @@ -0,0 +1,39 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "database" + +describe ::Openstack do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS + @chef_run.converge "openstack-common::default" + @subject = ::Object.new.extend ::Openstack + @subject.stub :include_recipe + end + + describe "#db_create_with_user" do + it "returns nil when no such service was found" do + @subject.stub(:node).and_return @chef_run.node + @subject.db_create_with_user("nonexisting", "user", "pass").should be_nil + end + + it "returns db info and creates database with user when service found" do + @subject.stub(:database).and_return {} + @subject.stub(:database_user).and_return {} + @subject.stub(:node).and_return @chef_run.node + result = @subject.db_create_with_user "compute", "user", "pass" + result['host'].should == "127.0.0.1" + result['port'].should == "3306" + end + + it "creates database" do + pending "TODO: test this LWRP" + end + + it "creates database user" do + pending "TODO: test this LWRP" + end + + it "grants privs to database user" do + pending "TODO: test this LWRP" + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/default-suse_spec.rb b/chef/cookbooks/openstack-common/spec/default-suse_spec.rb new file mode 100644 index 0000000..d0d0118 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/default-suse_spec.rb @@ -0,0 +1,9 @@ +require_relative "spec_helper" + +describe "openstack-common::default" do + describe "suse" do + it "configures openstack repository" do + pending "TODO: implement" + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/default_spec.rb b/chef/cookbooks/openstack-common/spec/default_spec.rb new file mode 100644 index 0000000..c250945 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/default_spec.rb @@ -0,0 +1,24 @@ +require_relative "spec_helper" + +describe "openstack-common::default" do + describe "ubuntu" do + before do + opts = ::UBUNTU_OPTS.merge :step_into => ["apt_repository"] + @chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| + n.set["lsb"]["codename"] = "precise" + end + @chef_run.converge "openstack-common::default" + end + + it "installs ubuntu-cloud-keyring package" do + expect(@chef_run).to install_package "ubuntu-cloud-keyring" + end + + it "configures openstack repository" do + file = "/etc/apt/sources.list.d/openstack-ppa.list" + expected = "deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/grizzly main" + + expect(@chef_run).to create_file_with_content file, expected + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/endpoints_spec.rb b/chef/cookbooks/openstack-common/spec/endpoints_spec.rb new file mode 100644 index 0000000..db49285 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/endpoints_spec.rb @@ -0,0 +1,133 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "endpoints" + +describe ::Openstack do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS + @chef_run.converge "openstack-common::default" + @subject = ::Object.new.extend ::Openstack + end + + describe "#endpoint" do + it "returns nil when no openstack.endpoints not in node attrs" do + @subject.stub(:node).and_return {} + @subject.endpoint("nonexisting").should be_nil + end + it "returns nil when no such endpoint was found" do + @subject.stub(:node).and_return @chef_run.node + @subject.endpoint("nonexisting").should be_nil + end + it "handles a URI needing escaped" do + uri_hash = { + "openstack" => { + "endpoints" => { + "compute-api" => { + "uri" => "http://localhost:8080/v2/%(tenant_id)s" + } + } + } + } + @subject.stub(:node).and_return uri_hash + result = @subject.endpoint "compute-api" + result.path.should == "/v2/%25(tenant_id)s" + end + it "returns endpoint URI object when uri key in endpoint hash" do + uri_hash = { + "openstack" => { + "endpoints" => { + "compute-api" => { + "uri" => "http://localhost:8080/path" + } + } + } + } + @subject.stub(:node).and_return uri_hash + result = @subject.endpoint "compute-api" + result.port.should == 8080 + end + it "returns endpoint URI string when uri key in endpoint hash and host also in hash" do + uri_hash = { + "openstack" => { + "endpoints" => { + "compute-api" => { + "uri" => "http://localhost", + "host" => "ignored" + } + } + } + } + @subject.stub(:node).and_return uri_hash + @subject.endpoint("compute-api").to_s.should == "http://localhost" + end + it "returns endpoint URI object when uri key not in endpoint hash but host is in hash" do + @subject.should_receive(:uri_from_hash).with({"host"=>"localhost", "port"=>"8080"}) + uri_hash = { + "openstack" => { + "endpoints" => { + "compute-api" => { + "host" => "localhost", + "port" => "8080" + } + } + } + } + @subject.stub(:node).and_return uri_hash + @subject.endpoint "compute-api" + end + end + + describe "#endpoints" do + it "does nothing when no endpoints" do + @subject.stub(:node).and_return {} + @subject.endpoints.should be_nil + end + it "does nothing when empty endpoints" do + @subject.stub(:node).and_return({"openstack" => { "endpoints" => {}}}) + @count = 0 + @subject.endpoints do | ep | + @count += 1 + end + @count.should == 0 + end + it "executes block count when have endpoints" do + @subject.stub(:node).and_return @chef_run.node + @count = 0 + @subject.endpoints do |ep| + @count += 1 + end + @count.should >= 1 + end + end + + describe "#db" do + it "returns nil when no openstack.db not in node attrs" do + @subject.stub(:node).and_return {} + @subject.db("nonexisting").should be_nil + end + it "returns nil when no such service was found" do + @subject.stub(:node).and_return @chef_run.node + @subject.db("nonexisting").should be_nil + end + it "returns db info hash when service found" do + @subject.stub(:node).and_return @chef_run.node + @subject.db("compute")['host'].should == "127.0.0.1" + @subject.db("compute").has_key?("uri").should be_false + end + end + + describe "#db_uri" do + it "returns nil when no openstack.db not in node attrs" do + @subject.stub(:node).and_return {} + @subject.db_uri("nonexisting", "user", "pass").should be_nil + end + it "returns nil when no such service was found" do + @subject.stub(:node).and_return @chef_run.node + @subject.db_uri("nonexisting", "user", "pass").should be_nil + end + it "returns db info hash when service found" do + @subject.stub(:node).and_return @chef_run.node + expect = "mysql://user:pass@127.0.0.1:3306/nova" + @subject.db_uri("compute", "user", "pass").should == expect + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/logging_spec.rb b/chef/cookbooks/openstack-common/spec/logging_spec.rb new file mode 100644 index 0000000..437973e --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/logging_spec.rb @@ -0,0 +1,56 @@ +require_relative "spec_helper" + +describe "openstack-common::logging" do + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-common::logging" + end + + describe "/etc/openstack" do + before do + @dir = @chef_run.directory "/etc/openstack" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "755" + end + end + + describe "logging.conf" do + before do + @file = "/etc/openstack/logging.conf" + end + + it "has proper owner" do + expect(@chef_run.template(@file)).to be_owned_by "root", "root" + end + + it "has proper modes" do + m = @chef_run.template(@file).mode + expect(sprintf("%o", m)).to eq "644" + end + + it "templates openstack.logging.ignore block" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-common::logging" + node = chef_run.node + node.set["openstack"]["logging"]["ignore"] = { + "test.nova.api.openstack.wsgi" => "WARNING" + } + + tmp = [ + "[logger_test_nova_api_openstack_wsgi]", + "level = WARNING", + "handlers = prod,debug", + "qualname = test.nova.api.openstack.wsgi" + ] + expect(chef_run).to create_file_with_content @file, tmp.join("\n") + end + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/network_spec.rb b/chef/cookbooks/openstack-common/spec/network_spec.rb new file mode 100644 index 0000000..65977b5 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/network_spec.rb @@ -0,0 +1,46 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "network" + +describe ::Openstack do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["network"] = { + "interfaces" => { + "lo" => { + "addresses" => { + "127.0.0.1"=> { + "family" => "inet", + "prefixlen" => "8", + "netmask" => "255.0.0.0", + "scope" => "Node" + }, + "::1" => { + "family" => "inet6", + "prefixlen" => "128", + "scope" => "Node" + } + } + } + } + } + end + @chef_run.converge "openstack-common::default" + @subject = ::Object.new.extend ::Openstack + end + + describe "#address_for" do + it "returns ipv4 address" do + @subject.stub(:node).and_return @chef_run.node + resp = @subject.address_for "lo" + + expect(resp).to eq "127.0.0.1" + end + + it "returns ipv4 address" do + @subject.stub(:node).and_return @chef_run.node + resp = @subject.address_for "lo", "inet6" + + expect(resp).to eq "::1" + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/parse_spec.rb b/chef/cookbooks/openstack-common/spec/parse_spec.rb new file mode 100644 index 0000000..2f8aee4 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/parse_spec.rb @@ -0,0 +1,79 @@ +require_relative "spec_helper" +require "uri" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "parse" + +describe ::Openstack do + before do + @subject = ::Object.new.extend(::Openstack) + end + + describe "#prettytable_to_array" do + it "returns [] when no table provided" do + @subject.prettytable_to_array(nil).should == [] + end + it "returns [] when table provided is empty" do + @subject.prettytable_to_array("").should == [] + end + it "returns proper array of hashes when proper table provided" do + table = +"+---------+----------------------------------+----------------------------------+ +| tenant | access | secret | ++---------+----------------------------------+----------------------------------+ +| service | 91af731b3be244beb8f30fc59b7bc96d | ce811442cfb549c39390a203778a4bf5 | ++---------+----------------------------------+----------------------------------+" + @subject.prettytable_to_array(table).should == + [{"tenant" => "service", + "access" => "91af731b3be244beb8f30fc59b7bc96d", + "secret" => "ce811442cfb549c39390a203778a4bf5"}] + end + it "returns proper array of hashes when proper table provided including whitespace" do + table = +"+---------+----------------------------------+----------------------------------+ +| tenant | access | secret | ++---------+----------------------------------+----------------------------------+ +| service | 91af731b3be244beb8f30fc59b7bc96d | ce811442cfb549c39390a203778a4bf5 | ++---------+----------------------------------+----------------------------------+ + + +" + @subject.prettytable_to_array(table).should == + [{"tenant" => "service", + "access" => "91af731b3be244beb8f30fc59b7bc96d", + "secret" => "ce811442cfb549c39390a203778a4bf5"}] + end + it "returns a flatten hash when provided a Property/Value table" do + table = +"+-----------+----------------------------------+ +| Property | Value | ++-----------+----------------------------------+ +| access | 91af731b3be244beb8f30fc59b7bc96d | +| secret | ce811442cfb549c39390a203778a4bf5 | +| tenant_id | 429271dd1cf54b7ca921a0017524d8ea | +| user_id | 1c4fc229560f40689c490c5d0838fd84 | ++-----------+----------------------------------+" + @subject.prettytable_to_array(table).should == + [{"tenant_id" => "429271dd1cf54b7ca921a0017524d8ea", + "access" => "91af731b3be244beb8f30fc59b7bc96d", + "secret" => "ce811442cfb549c39390a203778a4bf5", + "user_id" => "1c4fc229560f40689c490c5d0838fd84"}] + end + it "returns a flatten hash when provided a Property/Value table including whitespace" do + table = +" + ++-----------+----------------------------------+ +| Property | Value | ++-----------+----------------------------------+ +| access | 91af731b3be244beb8f30fc59b7bc96d | +| secret | ce811442cfb549c39390a203778a4bf5 | +| tenant_id | 429271dd1cf54b7ca921a0017524d8ea | +| user_id | 1c4fc229560f40689c490c5d0838fd84 | ++-----------+----------------------------------+" + @subject.prettytable_to_array(table).should == + [{"tenant_id" => "429271dd1cf54b7ca921a0017524d8ea", + "access" => "91af731b3be244beb8f30fc59b7bc96d", + "secret" => "ce811442cfb549c39390a203778a4bf5", + "user_id" => "1c4fc229560f40689c490c5d0838fd84"}] + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/password_spec.rb b/chef/cookbooks/openstack-common/spec/password_spec.rb new file mode 100644 index 0000000..0e8f318 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/password_spec.rb @@ -0,0 +1,90 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "passwords" + +describe ::Openstack do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::CHEFSPEC_OPTS + @chef_run.converge "openstack-common::default" + @subject = ::Object.new.extend(::Openstack) + end + + describe "#secret" do + it "returns index param when developer_mode is true" do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["openstack"]["developer_mode"] = true + end + @chef_run.converge "openstack-common::default" + @subject.stub(:node).and_return @chef_run.node + result = @subject.secret("passwords", "nova") + result.should == "nova" + end + it "returns databag when developer_mode is false" do + value = {"nova" => "this"} + ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" + ::Chef::EncryptedDataBagItem.stub(:load).with("passwords", "nova", "secret").and_return value + @subject.stub(:node).and_return @chef_run.node + result = @subject.secret("passwords", "nova") + result.should == "this" + end + end + + describe "#service_password" do + it "returns index param when developer_mode is true" do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["openstack"]["developer_mode"] = true + end + @chef_run.converge "openstack-common::default" + @subject.stub(:node).and_return @chef_run.node + result = @subject.service_password("nova") + result.should == "nova" + end + it "returns databag when developer_mode is false" do + value = {"nova" => "this"} + ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" + ::Chef::EncryptedDataBagItem.stub(:load).with("service_passwords", "nova", "secret").and_return value + @subject.stub(:node).and_return @chef_run.node + result = @subject.service_password("nova") + result.should == "this" + end + end + + describe "#db_password" do + it "returns index param when developer_mode is true" do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["openstack"]["developer_mode"] = true + end + @chef_run.converge "openstack-common::default" + @subject.stub(:node).and_return @chef_run.node + result = @subject.db_password("nova") + result.should == "nova" + end + it "returns databag when developer_mode is false" do + value = {"nova" => "this"} + ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" + ::Chef::EncryptedDataBagItem.stub(:load).with("db_passwords", "nova", "secret").and_return value + @subject.stub(:node).and_return @chef_run.node + result = @subject.db_password("nova") + result.should == "this" + end + end + + describe "#user_password" do + it "returns index param when developer_mode is true" do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["openstack"]["developer_mode"] = true + end + @chef_run.converge "openstack-common::default" + @subject.stub(:node).and_return @chef_run.node + result = @subject.user_password("nova") + result.should == "nova" + end + it "returns databag when developer_mode is false" do + value = {"nova" => "this"} + ::Chef::EncryptedDataBagItem.stub(:load_secret).with("/etc/chef/openstack_data_bag_secret").and_return "secret" + ::Chef::EncryptedDataBagItem.stub(:load).with("user_passwords", "nova", "secret").and_return value + @subject.stub(:node).and_return @chef_run.node + result = @subject.user_password("nova") + result.should == "this" + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/search_spec.rb b/chef/cookbooks/openstack-common/spec/search_spec.rb new file mode 100644 index 0000000..c52da1e --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/search_spec.rb @@ -0,0 +1,140 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "search" + +describe ::Openstack do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::CHEFSPEC_OPTS) do |n| + n.set["openstack"]["mq"] = { + "server_role" => "openstack-ops-mq", + "port" => 5672 + } + end + @chef_run.converge "openstack-common::default" + @subject = ::Object.new.extend ::Openstack + end + + describe "#search_for" do + it "returns results" do + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search). + with(:node, "(chef_environment:_default AND roles:role) OR (chef_environment:_default AND recipes:role)"). + and_return [@chef_run.node] + resp = @subject.search_for("role") + + expect(resp[0]['fqdn']).to eq "chefspec.local" + end + + it "returns empty results" do + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search). + with(:node, "(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)"). + and_return [] + resp = @subject.search_for("empty-role") + + expect(resp).to eq [] + end + + it "always returns empty results" do + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search). + with(:node, "(chef_environment:_default AND roles:empty-role) OR (chef_environment:_default AND recipes:empty-role)"). + and_return nil + resp = @subject.search_for("empty-role") + + expect(resp).to eq [] + end + end + + describe "#memcached_servers" do + it "returns memcached list" do + nodes = [ + { "memcached" => { "listen" => "1.1.1.1", "port" => "11211" }}, + { "memcached" => { "listen" => "2.2.2.2", "port" => "11211" }} + ] + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search_for). + with("role"). + and_return nodes + resp = @subject.memcached_servers("role") + + expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211"] + end + + it "returns sorted memcached list" do + nodes = [ + { "memcached" => { "listen" => "3.3.3.3", "port" => "11211" }}, + { "memcached" => { "listen" => "1.1.1.1", "port" => "11211" }}, + { "memcached" => { "listen" => "2.2.2.2", "port" => "11211" }} + ] + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search_for). + with("role"). + and_return nodes + resp = @subject.memcached_servers("role") + + expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211", "3.3.3.3:11211"] + end + + it "returns memcached servers as defined by attributes" do + nodes = { + "openstack" => { + "memcached_servers" => ["1.1.1.1:11211", "2.2.2.2:11211"] + } + } + @subject.stub(:node).and_return @chef_run.node.merge nodes + resp = @subject.memcached_servers("role") + + expect(resp).to eq ["1.1.1.1:11211", "2.2.2.2:11211"] + end + + it "returns empty memcached servers as defined by attributes" do + nodes = { + "openstack" => { + "memcached_servers" => [] + } + } + @subject.stub(:node).and_return @chef_run.node.merge nodes + resp = @subject.memcached_servers("empty-role") + + expect(resp).to eq [] + end + end + + describe "#rabbit_servers" do + it "returns rabbit servers" do + nodes = [ + { "openstack" => { "mq" => { "listen" => "1.1.1.1", "port" => "5672" }}}, + { "openstack" => { "mq" => { "listen" => "2.2.2.2", "port" => "5672" }}}, + ] + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search_for). + and_return nodes + resp = @subject.rabbit_servers + + expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672" + end + + it "returns sorted rabbit servers" do + nodes = [ + { "openstack" => { "mq" => { "listen" => "3.3.3.3", "port" => "5672" }}}, + { "openstack" => { "mq" => { "listen" => "1.1.1.1", "port" => "5672" }}}, + { "openstack" => { "mq" => { "listen" => "2.2.2.2", "port" => "5672" }}} + ] + @subject.stub(:node).and_return @chef_run.node + @subject.stub(:search_for). + and_return nodes + resp = @subject.rabbit_servers + + expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672,3.3.3.3:5672" + end + + it "returns rabbit servers when not searching" do + node = @chef_run.node + node.set["openstack"]["mq"]["servers"] = ["1.1.1.1", "2.2.2.2"] + @subject.stub(:node).and_return @chef_run.node + resp = @subject.rabbit_servers + + expect(resp).to eq "1.1.1.1:5672,2.2.2.2:5672" + end + end +end diff --git a/chef/cookbooks/openstack-common/spec/spec_helper.rb b/chef/cookbooks/openstack-common/spec/spec_helper.rb new file mode 100644 index 0000000..67f67be --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/spec_helper.rb @@ -0,0 +1,11 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} +::CHEFSPEC_OPTS = { + :log_level => ::LOG_LEVEL +} diff --git a/chef/cookbooks/openstack-common/spec/uri_spec.rb b/chef/cookbooks/openstack-common/spec/uri_spec.rb new file mode 100644 index 0000000..a760450 --- /dev/null +++ b/chef/cookbooks/openstack-common/spec/uri_spec.rb @@ -0,0 +1,85 @@ +require_relative "spec_helper" +require ::File.join ::File.dirname(__FILE__), "..", "libraries", "uri" +require "uri" + +describe ::Openstack do + before do + @subject = ::Object.new.extend(::Openstack) + end + + describe "#uri_from_hash" do + it "returns nil when no host or uri key found" do + hash = { + "port" => 8888, + "path" => "/path" + } + @subject.uri_from_hash(hash).should be_nil + end + it "returns uri when uri key found, ignoring other parts" do + uri = "http://localhost/" + hash = { + "port" => 8888, + "path" => "/path", + "uri" => uri + } + result = @subject.uri_from_hash(hash) + result.should be_a URI + result.to_s.should == uri + end + it "constructs from host" do + uri = "https://localhost:8888/path" + hash = { + "scheme" => 'https', + "port" => 8888, + "path" => "/path", + "host" => "localhost" + } + result = @subject.uri_from_hash(hash) + result.to_s.should == uri + end + it "constructs with defaults" do + uri = "https://localhost" + hash = { + "scheme" => 'https', + "host" => "localhost" + } + result = @subject.uri_from_hash(hash) + result.to_s.should == uri + end + it "constructs with extraneous keys" do + uri = "http://localhost" + hash = { + "host" => "localhost", + "network" => "public" # To emulate the osops-utils::ip_location way... + } + result = @subject.uri_from_hash(hash) + result.to_s.should == uri + end + end + + describe "#uri_join_paths" do + it "returns nil when no paths are passed in" do + @subject.uri_join_paths().should be_nil + end + it "preserves absolute path when only absolute path passed in" do + path = "/abspath" + result = @subject.uri_join_paths(path) + result.should == path + end + it "preserves relative path when only relative path passed in" do + path = "abspath/" + result = @subject.uri_join_paths(path) + result.should == path + end + it "preserves leadng and trailing slashes" do + expected = "/path/to/resource/" + result = @subject.uri_join_paths("/path", "to", "resource/") + result.should == expected + end + it "removes extraneous intermediate slashes" do + expected = "/path/to/resource" + result = @subject.uri_join_paths("/path", "//to/", "/resource") + result.should == expected + end + end +end diff --git a/chef/cookbooks/openstack-common/templates/default/logging.conf.erb b/chef/cookbooks/openstack-common/templates/default/logging.conf.erb new file mode 100644 index 0000000..f45a888 --- /dev/null +++ b/chef/cookbooks/openstack-common/templates/default/logging.conf.erb @@ -0,0 +1,136 @@ +[loggers] +keys=root,ceilometer,cinder,glance,horizon,keystone,nova,quantum,swift,amqplib,sqlalchemy,boto,suds,eventletwsgi,<%= node["openstack"]["logging"]["ignore"].map{|k,v| k.gsub(/\W/, '_')}.join(',') %> + +[formatters] +keys=normal,normal_with_name,debug,syslog_with_name,syslog_debug + +[handlers] +keys=stderr,devel,prod,debug + +## FORMATTERS ## + +[formatter_debug] +format=[%(name)s]: %(asctime)s %(levelname)s %(module)s.%(funcName)s %(message)s + +[formatter_normal] +format=%(asctime)s %(levelname)s %(message)s + +[formatter_normal_with_name] +format=[%(name)s]: %(asctime)s %(levelname)s %(message)s + +[formatter_syslog_with_name] +format=%(name)s: %(levelname)s %(message)s + +[formatter_syslog_debug] +format=%(name)s: %(levelname)s %(module)s.%(funcName)s %(message)s + +## LOGGERS ## + +[logger_amqplib] +level = WARNING +handlers = stderr +qualname = amqplib + +[logger_sqlalchemy] +level = WARNING +handlers = stderr +qualname = sqlalchemy +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARNING" logs neither. (Recommended for production systems.) + +[logger_boto] +level = WARNING +handlers = stderr +qualname = boto + +[logger_suds] +level = INFO +handlers = stderr +qualname = suds + +<% node["openstack"]["logging"]["ignore"].each do |k,v| %> +[logger_<%= k.gsub(/\W/, '_') %>] +level = <%= v %> +handlers = prod,debug +qualname = <%= k %> + +<% end %> +[logger_eventletwsgi] +level = WARNING +handlers = stderr +qualname = eventlet.wsgi.server + +[logger_root] +level=NOTSET +handlers=devel + +[logger_ceilometer] +level=DEBUG +handlers=prod,debug +qualname=ceilometer + +[logger_cinder] +level=DEBUG +handlers=prod,debug +qualname=cinder + +[logger_glance] +level=DEBUG +handlers=prod,debug +qualname=glance + +[logger_horizon] +level=DEBUG +handlers=prod,debug +qualname=horizon + +[logger_keystone] +level=DEBUG +handlers=prod,debug +qualname=keystone + +[logger_nova] +level=DEBUG +handlers=prod,debug +qualname=nova + +[logger_quantum] +level=DEBUG +handlers=prod,debug +qualname=quantum + +[logger_swift] +level=DEBUG +handlers=prod,debug +qualname=swift + +## HANDLERS ## + +[handler_stderr] +class = StreamHandler +args = (sys.stderr,) +formatter = debug + +[handler_devel] +class=StreamHandler +level=NOTSET +formatter=debug +args=(sys.stdout,) + +[handler_file] +class=FileHandler +formatter=debug +args=('/var/log/openstack/openstack.log', 'w') + +[handler_prod] +level=INFO +class=handlers.SysLogHandler +formatter=syslog_with_name +args=(('/dev/log'), handlers.SysLogHandler.LOG_LOCAL0) + +[handler_debug] +level=DEBUG +class=handlers.SysLogHandler +formatter=syslog_debug +args=(('/dev/log'), handlers.SysLogHandler.LOG_LOCAL1) diff --git a/chef/cookbooks/openstack-compute/.tailor b/chef/cookbooks/openstack-compute/.tailor new file mode 100644 index 0000000..0b41998 --- /dev/null +++ b/chef/cookbooks/openstack-compute/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 50, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-compute/Berksfile b/chef/cookbooks/openstack-compute/Berksfile new file mode 100644 index 0000000..046458a --- /dev/null +++ b/chef/cookbooks/openstack-compute/Berksfile @@ -0,0 +1,12 @@ +metadata + +cookbook "openstack-image", + git: "git://github.com/stackforge/cookbook-openstack-image.git" +cookbook "openstack-identity", + git: "git://github.com/stackforge/cookbook-openstack-identity.git" +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" +cookbook "openstack-network", + git: "git://github.com/stackforge/cookbook-openstack-network.git" +cookbook "sysctl", + git: "git://github.com/Fewbytes/sysctl-cookbook.git" diff --git a/chef/cookbooks/openstack-compute/Berksfile.lock b/chef/cookbooks/openstack-compute/Berksfile.lock new file mode 100644 index 0000000..1b4bc08 --- /dev/null +++ b/chef/cookbooks/openstack-compute/Berksfile.lock @@ -0,0 +1,65 @@ +{ + "sources": { + "openstack-compute": { + "path": "." + }, + "openstack-image": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-image.git", + "ref": "c06c440a371e4b8602a2de54b21e4c6a82a0fbf3" + }, + "openstack-identity": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-identity.git", + "ref": "b881af26095cfa869a6970067c49597a0ee63586" + }, + "openstack-common": { + "locked_version": "0.4.3", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" + }, + "openstack-network": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-network.git", + "ref": "2b6ecb00e81e98765343ecb4d8655c5d74fd46c9" + }, + "sysctl": { + "locked_version": "0.2.0", + "git": "git://github.com/Fewbytes/sysctl-cookbook.git", + "ref": "65a96b45d489c904515d916aae6bc474da35f1ca" + }, + "selinux": { + "locked_version": "0.5.6" + }, + "yum": { + "locked_version": "2.3.0" + }, + "python": { + "locked_version": "1.3.6" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "apt": { + "locked_version": "2.0.0" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-compute/Gemfile b/chef/cookbooks/openstack-compute/Gemfile new file mode 100644 index 0000000..04ef97e --- /dev/null +++ b/chef/cookbooks/openstack-compute/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-compute/Gemfile.lock b/chef/cookbooks/openstack-compute/Gemfile.lock new file mode 100644 index 0000000..f1a0a65 --- /dev/null +++ b/chef/cookbooks/openstack-compute/Gemfile.lock @@ -0,0 +1,214 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.7) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.9.0) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.8) + multipart-post (~> 1.2.0) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.2.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.4) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.2.0) + multi_json (1.7.7) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.9) + net-ssh (2.6.8) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.18.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.5) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.4) + rspec-expectations (2.14.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.2) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.8.0) + strainer (3.1.1) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.3) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.1.1) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-compute/README.md b/chef/cookbooks/openstack-compute/README.md new file mode 100644 index 0000000..f3d999b --- /dev/null +++ b/chef/cookbooks/openstack-compute/README.md @@ -0,0 +1,256 @@ +Description +=========== + +This cookbook installs the OpenStack Compute service **Nova** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Nova is currently installed from packages. + +http://nova.openstack.org + +Requirements +============ + +Chef 0.10.0 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* apache2 +* openstack-common +* openstack-identity +* openstack-image +* selinux (Fedora) +* sysctl +* yum + +Usage +===== + +api-ec2 +---- +- Includes recipe `nova-common` +- Installs AWS EC2 compatible API and configures the service and endpoints in keystone + +api-metadata +---- +- Includes recipe `nova-common` +- Installs the nova metadata package + +api-os-compute +---- +- Includes recipe `nova-common` +- Installs OS API and configures the service and endpoints in keystone + +compute +---- +- Includes recipes `nova-common`, `api-metadata`, `network` +- Installs nova-compute service + +libvirt +---- +- Installs libvirt, used by nova compute for management of the virtual machine environment + +network +---- +- Includes recipe `nova-common` +- Installs nova network service + +nova-cert +---- +- Installs nova-cert service + +nova-common +---- +- May include recipe `selinux` (Fedora) +- Builds the basic nova.conf config file with details of the rabbitmq, mysql, glance and keystone servers +- Builds a openrc file for root with appropriate environment variables to interact with the nova client CLI + +nova-setup +---- +- Includes recipes `nova-common` +- Sets up the nova networks with `nova-manage` + +scheduler +---- +- Includes recipe `nova-common` +- Installs nova scheduler service + +vncproxy +---- +- Includes recipe `nova-common` +- Installs and configures the vncproxy service for console access to VMs + +Attributes +========== + +Openstack Compute attributes are in the attribute namespace ["openstack"]["compute"]. + +* `openstack["compute"]["identity_service_chef_role"]` - The name of the Chef role that sets up the Keystone Service API +* `openstack["compute"]["user"]` - User nova services run as +* `openstack["compute"]["group"]` - Group nova services run as +* `openstack["compute"]["db"]["username"]` - Username for nova database access +* `openstack["compute"]["rabbit"]["username"]` - Username for nova rabbit access +* `openstack["compute"]["rabbit"]["vhost"]` - The rabbit vhost to use +* `openstack["compute"]["rabbit"]["port"]` - The rabbit port to use +* `openstack["compute"]["rabbit"]["host"]` - The rabbit host to use (must set when `openstack["compute"]["rabbit"]["ha"]` false). +* `openstack["compute"]["rabbit"]["ha"]` - Whether or not to use rabbit ha +* `openstack["compute"]["service_tenant_name"]` - Tenant name used by nova when interacting with keystone +* `openstack["compute"]["service_user"]` - User name used by nova when interacting with keystone +* `openstack["compute"]["service_role"]` - User role used by nova when interacting with keystone +* `openstack["compute"]["floating_cmd"]` - Path to the `nova-manage floating create` wrapper script. +* `openstack["compute"]["config"]["volume_api_class"]` - API Class used for Volume support +* `openstack["compute"]["compute"]["api"]["protocol"]` - Protocol used for the OS API +* `openstack["compute"]["compute"]["api"]["port"]` - Port on which OS API runs +* `openstack["compute"]["compute"]["api"]["version"]` - Version of the OS API used +* `openstack["compute"]["compute"]["adminURL"]` - URL used to access the OS API for admin functions +* `openstack["compute"]["compute"]["internalURL"]` - URL used to access the OS API for user functions from an internal network +* `openstack["compute"]["compute"]["publicURL"]` - URL used to access the OS API for user functions from an external network +* `openstack["compute"]["config"]["availability_zone"]` - Nova availability zone. Usually set at the node level to place a compute node in another az +* `openstack["compute"]["config"]["default_schedule_zone"]` - The availability zone to schedule instances in when no az is specified in the request +* `openstack["compute"]["config"]["force_raw_images"]` - Convert all images used as backing files for instances to raw (we default to false) +* `openstack["compute"]["config"]["allow_same_net_traffic"]` - Disable security groups for internal networks (we default to true) +* `openstack["compute"]["config"]["osapi_max_limit"]` - The maximum number of items returned in a single response from a collection resource (default is 1000) +* `openstack["compute"]["config"]["cpu_allocation_ratio"]` - Virtual CPU to Physical CPU allocation ratio (default 16.0) +* `openstack["compute"]["config"]["ram_allocation_ratio"]` - Virtual RAM to Physical RAM allocation ratio (default 1.5) +* `openstack["compute"]["config"]["snapshot_image_format"]` - Snapshot image format (valid options are : raw, qcow2, vmdk, vdi [we default to qcow2]). +* `openstack["compute"]["config"]["start_guests_on_host_boot"]` - Whether to restart guests when the host reboots +* `openstack["compute"]["config"]["resume_guests_state_on_host_boot"]` - Whether to start guests that were running before the host rebooted +* `openstack["compute"]["api"]["signing_dir"]` - Keystone PKI needs a location to hold the signed tokens +* `openstack["compute"]["api"]["signing_dir"]` - Keystone PKI needs a location to hold the signed tokens + +Networking Attributes +--------------------- + +Basic networking configuration is controlled with the following attributes: + +* `openstack["compute"]["network"]["network_manager"]` - Defaults to "nova.network.manager.FlatDHCPManager". Set to "nova.network.manager.VlanManager" to configure VLAN Networking. +* `openstack["compute"]["network"]["fixed_range"]` - The CIDR for the network that VMs will be assigned to. In the case of VLAN Networking, this should be the network in which all VLAN networks that tenants are assigned will fit. +* `openstack["compute"]["network"]["dmz_cidr"]` - A CIDR for the range of IP addresses that will NOT be SNAT'ed by the nova network controller +* `openstack["compute"]["network"]["public_interface"]` - Defaults to eth0. Refers to the network interface used for VM addresses in the `fixed_range`. +* `openstack["compute"]["network"]["vlan_interface"]` - Defaults to eth0. Refers to the network interface used for VM addresses when VMs are assigned in a VLAN subnet. + +You can have the cookbook automatically create networks in Nova for you by adding a Hash to the `openstack["compute"]["networks"]` Array. +**Note**: The `openstack-compute::nova-setup` recipe contains the code that creates these pre-defined networks. + +Each Hash must contain the following keys: + +* `ipv4_cidr` - The CIDR representation of the subnet. Supplied to the nova-manage network create command as `--fixed_ipv4_range` +* `label` - A name for the network + +In addition to the above required keys in the Hash, the below keys are optional: + +* `num_networks` - Passed as-is to `nova-manage network create` as the `--num_networks` option. This overrides the default `num_networks` nova.conf value. +* `network_size` - Passed as-is to `nova-manage network create` as the `--network_size` option. This overrides the default `network_size` nova.conf value. +* `bridge` - Passed as-is to `nova-manage network create` as the `--bridge` option. +* `bridge_interface` -- Passed as-is to `nova-manage network create` as the `--bridge_interface` option. This overrides the default `vlan_interface` nova.conf value. +* `dns1` - Passed as-is to `nova-manage network create` as the `--dns1` option. +* `dns2` - Passed as-is to `nova-manage network create` as the `--dns2` option. +* `multi_host` - Passed as-is to `nova-manage network create` as the `--multi_host` option. Values should be either 'T' or 'F' +* `vlan` - Passed as-is to `nova-manage network create` as the `--vlan` option. Should be the VLAN tag ID. + +By default, the `openstack["compute"]["networks"]` array has two networks: + +* `openstack["compute"]["networks"]["public"]["label"]` - Network label to be assigned to the public network on creation +* `openstack["compute"]["networks"]["public"]["ipv4_cidr"]` - Network to be created (in CIDR notation, e.g., 192.168.100.0/24) +* `openstack["compute"]["networks"]["public"]["num_networks"]` - Number of networks to be created +* `openstack["compute"]["networks"]["public"]["network_size"]` - Number of IP addresses to be used in this network +* `openstack["compute"]["networks"]["public"]["bridge"]` - Bridge to be created for accessing the VM network (e.g., br100) +* `openstack["compute"]["networks"]["public"]["bridge_dev"]` - Physical device on which the bridge device should be attached (e.g., eth2) +* `openstack["compute"]["networks"]["public"]["dns1"]` - DNS server 1 +* `openstack["compute"]["networks"]["public"]["dns2"]` - DNS server 2 + +* `openstack["compute"]["networks"]["private"]["label"]` - Network label to be assigned to the private network on creation +* `openstack["compute"]["networks"]["private"]["ipv4_cidr"]` - Network to be created (in CIDR notation e.g., 192.168.200.0/24) +* `openstack["compute"]["networks"]["private"]["num_networks"]` - Number of networks to be created +* `openstack["compute"]["networks"]["private"]["network_size"]` - Number of IP addresses to be used in this network +* `openstack["compute"]["networks"]["private"]["bridge"]` - Bridge to be created for accessing the VM network (e.g., br200) +* `openstack["compute"]["networks"]["private"]["bridge_dev"]` - Physical device on which the bridge device should be attached (e.g., eth3) + +VNC Configuration Attributes +---------------------------- + +Requires [network_addr](https://gist.github.com/jtimberman/1040543) Ohai plugin. + +* `openstack["compute"]["xvpvnc_proxy"]["service_port"]` - Port on which XvpVNC runs +* `openstack["compute"]["xvpvnc_proxy"]["bind_interface"]` - Determine the interface's IP address to bind to +* `openstack["compute"]["novnc_proxy"]["service_port"]` - Port on which NoVNC runs +* `openstack["compute"]["novnc_proxy"]["bind_interface"]` - Determine the interface's IP address to bind to + +Libvirt Configuration Attributes +--------------------------------- + +* `openstack["compute"]["libvirt"]["virt_type"]` - What hypervisor software layer to use with libvirt (e.g., kvm, qemu) +* `openstack["compute"]["libvirt"]["bind_interface"]` - Determine the interface's IP address (used for VNC). IP address on the hypervisor that libvirt listens for VNC requests on, and IP address on the hypervisor that libvirt exposes for VNC requests on. +* `openstack["compute"]["libvirt"]["auth_tcp"]` - Type of authentication your libvirt layer requires +* `openstack["compute"]["libvirt"]["ssh"]["private_key"]` - Private key to use if using SSH authentication to your libvirt layer +* `openstack["compute"]["libvirt"]["ssh"]["public_key"]` - Public key to use if using SSH authentication to your libvirt layer + +Scheduler Configuration Attributes +---------------------------------- + +* `openstack["compute"]["scheduler"]["scheduler_driver"]` - the scheduler driver to use +NOTE: The filter scheduler currently does not work with ec2. +* `openstack["compute"]["scheduler"]["default_filters"]` - a list of filters enabled for schedulers that support them. + +Syslog Configuration Attributes +------------------------------- + +* `openstack["compute"]["syslog"]["use"]` - Should nova log to syslog? +* `openstack["compute"]["syslog"]["facility"]` - Which facility nova should use when logging in python style (for example, `LOG_LOCAL1`) +* `openstack["compute"]["syslog"]["config_facility"]` - Which facility nova should use when logging in rsyslog style (for example, local1) + +OSAPI Compute Extentions +------------------------ + +* `openstack["compute"]["plugins"]` - Array of osapi compute exntesions to add to nova + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Justin Shepherd () | +| **Author** | Jason Cannavale () | +| **Author** | Ron Pedde () | +| **Author** | Joseph Breu () | +| **Author** | William Kelly () | +| **Author** | Darren Birkett () | +| **Author** | Evan Callicoat () | +| **Author** | Matt Ray () | +| **Author** | Jay Pipes () | +| **Author** | John Dewey () | +| **Author** | Kevin Bringard () | +| **Author** | Craig Tracey () | +| **Author** | Sean Gallagher () | +| **Author** | Ionut Artarisi () | +| | | +| **Copyright** | Copyright (c) 2012-2013, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2012-2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, Craig Tracey | +| **Copyright** | Copyright (c) 2013, SUSE Linux GmbH | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-compute/Strainerfile b/chef/cookbooks/openstack-compute/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-compute/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-compute/attributes/default.rb b/chef/cookbooks/openstack-compute/attributes/default.rb new file mode 100644 index 0000000..1189d43 --- /dev/null +++ b/chef/cookbooks/openstack-compute/attributes/default.rb @@ -0,0 +1,338 @@ +######################################################################## +# Toggles - These can be overridden at the environment level +default["enable_monit"] = false # OS provides packages +######################################################################## + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["compute"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +# The name of the Chef role that knows about the message queue server +# that Nova uses +default["openstack"]["compute"]["rabbit_server_chef_role"] = "os-ops-messaging" + +default["openstack"]["compute"]["verbose"] = "False" +default["openstack"]["compute"]["debug"] = "False" + +# The name of the Chef role that sets up the Keystone Service API +default["openstack"]["compute"]["identity_service_chef_role"] = "os-identity" + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# db_password routine. +default["openstack"]["compute"]["db"]["username"] = "nova" + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +default["openstack"]["compute"]["rabbit"]["username"] = "guest" +default["openstack"]["compute"]["rabbit"]["vhost"] = "/" +default["openstack"]["compute"]["rabbit"]["port"] = 5672 +default["openstack"]["compute"]["rabbit"]["host"] = "127.0.0.1" +default["openstack"]["compute"]["rabbit"]["ha"] = false + +default["openstack"]["compute"]["service_tenant_name"] = "service" +default["openstack"]["compute"]["service_user"] = "nova" +default["openstack"]["compute"]["service_role"] = "admin" + +case platform +when "fedora", "redhat", "centos", "ubuntu" + default["openstack"]["compute"]["user"] = "nova" + default["openstack"]["compute"]["group"] = "nova" +when "suse" + default["openstack"]["compute"]["user"] = "openstack-nova" + default["openstack"]["compute"]["group"] = "openstack-nova" +end + +# Logging stuff +default["openstack"]["compute"]["syslog"]["use"] = false +default["openstack"]["compute"]["syslog"]["facility"] = "LOG_LOCAL1" +default["openstack"]["compute"]["syslog"]["config_facility"] = "local1" + +default["openstack"]["compute"]["region"] = "RegionOne" + +default["openstack"]["compute"]["floating_cmd"] = "/usr/local/bin/add_floaters.py" + +# Support multiple network types. Default network type is "nova" +# with the other option supported being "quantum" +default["openstack"]["compute"]["network"]["service_type"] = "nova" + +# if the network type is not nova, we will load the following +# plugins from openstack-network +default["openstack"]["compute"]["network"]["plugins"] = ["openvswitch"] + +# Quantum options +default["openstack"]["compute"]["network"]["quantum"]["network_api_class"] = "nova.network.quantumv2.api.API" +default["openstack"]["compute"]["network"]["quantum"]["auth_strategy"] = "keystone" +default["openstack"]["compute"]["network"]["quantum"]["admin_tenant_name"] = "service" +default["openstack"]["compute"]["network"]["quantum"]["admin_username"] = "quantum" +default["openstack"]["compute"]["network"]["quantum"]["libvirt_vif_driver"] = "nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver" +default["openstack"]["compute"]["network"]["quantum"]["linuxnet_interface_driver"] = "nova.network.linux_net.LinuxOVSInterfaceDriver" +default["openstack"]["compute"]["network"]["quantum"]["security_group_api"] = "quantum" +default["openstack"]["compute"]["network"]["quantum"]["service_quantum_metadata_proxy"] = true +default["openstack"]["compute"]["network"]["quantum"]["metadata_secret_name"] = "quantum_metadata_shared_secret" +default["openstack"]["compute"]["network"]["quantum"]["public_network_name"] = "public" +default["openstack"]["compute"]["network"]["quantum"]["dns_server"] = "8.8.8.8" + +# TODO(shep): This should probably be ["openstack"]["compute"]["network"]["fixed"] +default["openstack"]["compute"]["networks"] = [ + { + "label" => "public", + "ipv4_cidr" => "192.168.100.0/24", + "num_networks" => "1", + "network_size" => "255", + "bridge" => "br100", + "bridge_dev" => "eth2", + "dns1" => "8.8.8.8", + "dns2" => "8.8.4.4", + "multi_host" => 'T' + }, + { + "label" => "private", + "ipv4_cidr" => "192.168.200.0/24", + "num_networks" => "1", + "network_size" => "255", + "bridge" => "br200", + "bridge_dev" => "eth3", + "dns1" => "8.8.8.8", + "dns2" => "8.8.4.4", + "multi_host" => 'T' + } +] + +# For VLAN Networking, do the following: +# +# default["openstack"]["compute"]["network"]["network_manager"] = "nova.network.manager.VlanManager" +# default["openstack"]["compute"]["network"]["vlan_interface"] = "eth1" # Or "eth2", "bond1", etc... +# # The fixed_range setting is the **entire** subnet/network that all your VLAN +# # networks will fit inside. +# default["openstack"]["compute"]["network"]["fixed_range"] = "10.0.0.0/8" # Or smaller for smaller deploys... +# +# In addition to the above, you typically either want to do one of the following: +# +# 1) Set default["openstack"]["compute"]["networks"] to an empty Array ([]) and create your +# VLAN networks (using nova-manage network create) **when you create a tenant**. +# +# 2) Set default["openstack"]["compute"]["networks"] to an Array of VLAN networks that get created +# **without a tenant assignment** for tenants to use when they are created later. +# Such an array might look like this: +# +# default["openstack"]["compute"]["networks"] = [ +# { +# "label": "vlan100", +# "vlan": "100", +# "ipv4_cidr": "10.0.100.0/24" +# }, +# { +# "label": "vlan101", +# "vlan": "101", +# "ipv4_cidr": "10.0.101.0/24" +# }, +# { +# "label": "vlan102", +# "vlan": "102", +# "ipv4_cidr": "10.0.102.0/24" +# }, +# ] + +default["openstack"]["compute"]["network"]["multi_host"] = false +default["openstack"]["compute"]["network"]["fixed_range"] = default["openstack"]["compute"]["networks"][0]["ipv4_cidr"] +# DMZ CIDR is a range of IP addresses that should not +# have their addresses SNAT'ed by the nova network controller +default["openstack"]["compute"]["network"]["dmz_cidr"] = "10.128.0.0/24" +default["openstack"]["compute"]["network"]["network_manager"] = "nova.network.manager.FlatDHCPManager" +default["openstack"]["compute"]["network"]["public_interface"] = "eth0" +default["openstack"]["compute"]["network"]["vlan_interface"] = "eth0" +# https://bugs.launchpad.net/nova/+bug/1075859 +default["openstack"]["compute"]["network"]["use_single_default_gateway"] = false + +default["openstack"]["compute"]["scheduler"]["scheduler_driver"] = "nova.scheduler.filter_scheduler.FilterScheduler" +default["openstack"]["compute"]["scheduler"]["default_filters"] = [ + "AvailabilityZoneFilter", + "RamFilter", + "ComputeFilter", + "CoreFilter", + "SameHostFilter", + "DifferentHostFilter" +] + +default["openstack"]["compute"]["xvpvnc_proxy"]["service_port"] = "6081" +default["openstack"]["compute"]["xvpvnc_proxy"]["bind_interface"] = "lo" +default["openstack"]["compute"]["novnc_proxy"]["service_port"] = "6080" +default["openstack"]["compute"]["novnc_proxy"]["bind_interface"] = "lo" + +default["openstack"]["compute"]["driver"] = "libvirt.LibvirtDriver" +default["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" +default["openstack"]["compute"]["libvirt"]["bind_interface"] = "lo" +default["openstack"]["compute"]["libvirt"]["auth_tcp"] = "none" +default["openstack"]["compute"]["libvirt"]["remove_unused_base_images"] = true +default["openstack"]["compute"]["libvirt"]["remove_unused_resized_minimum_age_seconds"] = 3600 +default["openstack"]["compute"]["libvirt"]["remove_unused_original_minimum_age_seconds"] = 3600 +default["openstack"]["compute"]["libvirt"]["checksum_base_images"] = false +if node["platform"] == "suse" + default["openstack"]["compute"]["libvirt"]["group"] = "libvirt" +else + default["openstack"]["compute"]["libvirt"]["group"] = "libvirtd" +end +default["openstack"]["compute"]["config"]["availability_zone"] = "nova" +default["openstack"]["compute"]["config"]["storage_availability_zone"] = "nova" +default["openstack"]["compute"]["config"]["default_schedule_zone"] = "nova" +default["openstack"]["compute"]["config"]["force_raw_images"] = false +default["openstack"]["compute"]["config"]["allow_same_net_traffic"] = true +default["openstack"]["compute"]["config"]["osapi_max_limit"] = 1000 +default["openstack"]["compute"]["config"]["cpu_allocation_ratio"] = 16.0 +default["openstack"]["compute"]["config"]["ram_allocation_ratio"] = 1.5 +default["openstack"]["compute"]["config"]["snapshot_image_format"] = "qcow2" +# `start` will cause nova-compute to error out if a VM is already running, where +# `resume` checks to see if it is running first. +default["openstack"]["compute"]["config"]["start_guests_on_host_boot"] = false +# requires https://review.openstack.org/#/c/8423/ +default["openstack"]["compute"]["config"]["resume_guests_state_on_host_boot"] = true + +# If true, create a config drive regardless of if the user specified --config-drive true in their nova boot call +default["openstack"]["compute"]["config"]["force_config_drive"] = "false" + +# Volume API class (driver) +default["openstack"]["compute"]["config"]["volume_api_class"] = "nova.volume.cinder.API" + +# quota settings +default["openstack"]["compute"]["config"]["quota_security_groups"] = 50 +default["openstack"]["compute"]["config"]["quota_security_group_rules"] = 20 +# (StrOpt) default driver to use for quota checks (default: nova.quota.DbQuotaDriver) +default["openstack"]["compute"]["config"]["quota_driver"] = "nova.quota.DbQuotaDriver" +# number of instance cores allowed per project (default: 20) +default["openstack"]["compute"]["config"]["quota_cores"] = 20 +# number of fixed ips allowed per project (this should be at least the number of instances allowed) (default: -1) +default["openstack"]["compute"]["config"]["quota_fixed_ips"] = -1 +# number of floating ips allowed per project (default: 10) +default["openstack"]["compute"]["config"]["quota_floating_ips"] = 10 +# number of bytes allowed per injected file (default: 10240) +default["openstack"]["compute"]["config"]["quota_injected_file_content_bytes"] = 10240 +# number of bytes allowed per injected file path (default: 255) +default["openstack"]["compute"]["config"]["quota_injected_file_path_bytes"] = 255 +# number of injected files allowed (default: 5) +default["openstack"]["compute"]["config"]["quota_injected_files"] = 5 +# number of instances allowed per project (defailt: 10) +default["openstack"]["compute"]["config"]["quota_instances"] = 10 +# number of key pairs per user (default: 100) +default["openstack"]["compute"]["config"]["quota_key_pairs"] = 100 +# number of metadata items allowed per instance (default: 128) +default["openstack"]["compute"]["config"]["quota_metadata_items"] = 128 +# megabytes of instance ram allowed per project (default: 51200) +default["openstack"]["compute"]["config"]["quota_ram"] = 51200 + +default["openstack"]["compute"]["ratelimit"]["settings"] = { + "generic-post-limit" => { "verb" => "POST", "uri" => "*", "regex" => ".*", "limit" => "10", "interval" => "MINUTE" }, + "create-servers-limit" => { "verb" => "POST", "uri" => "*/servers", "regex" => "^/servers", "limit" => "50", "interval" => "DAY" }, + "generic-put-limit" => { "verb" => "PUT", "uri" => "*", "regex" => ".*", "limit" => "10", "interval" => "MINUTE" }, + "changes-since-limit" => { "verb" => "GET", "uri" => "*changes-since*", "regex" => ".*changes-since.*", "limit" => "3", "interval" => "MINUTE" }, + "generic-delete-limit" => { "verb" => "DELETE", "uri" => "*", "regex" => ".*", "limit" => "100", "interval" => "MINUTE" } +} + +# Keystone settings +default["openstack"]["compute"]["api"]["auth_strategy"] = "keystone" + +# Setting this to v2.0. See discussion on +# https://bugs.launchpad.net/openstack-chef/+bug/1207504 +default["openstack"]["compute"]["api"]["auth"]["version"] = "v2.0" + +# Keystone PKI signing directories +default["openstack"]["compute"]["api"]["auth"]["cache_dir"] = "/var/cache/nova/api" + +# Perform nova-conductor operations locally (boolean value) +default["openstack"]["compute"]["conductor"]["use_local"] = "False" + +case platform +when "fedora", "redhat", "centos", "suse" # :pragma-foodcritic: ~FC024 - won't fix this + default["openstack"]["compute"]["platform"] = { + "api_ec2_packages" => ["openstack-nova-api"], + "api_ec2_service" => "openstack-nova-api", + "api_os_compute_packages" => ["openstack-nova-api"], + "api_os_compute_service" => "openstack-nova-api", + "api_os_compute_process_name" => "nova-api", + # "neutron_python_packages" => ["python-quantumclient", "python-pyparsing"], + "neutron_python_packages" => ["python-quantumclient", "pyparsing"], + "memcache_python_packages" => ["python-memcached"], + "compute_api_metadata_packages" => ["openstack-nova-api"], + "compute_api_metadata_process_name" => "nova-api", + "compute_api_metadata_service" => "openstack-nova-api", + "compute_compute_packages" => ["openstack-nova-compute"], + "compute_compute_service" => "openstack-nova-compute", + "compute_network_packages" => ["iptables", "openstack-nova-network"], + "compute_network_service" => "openstack-nova-network", + "compute_scheduler_packages" => ["openstack-nova-scheduler"], + "compute_scheduler_service" => "openstack-nova-scheduler", + "compute_conductor_packages" => ["openstack-nova-conductor"], + "compute_conductor_service" => "openstack-nova-conductor", + "compute_vncproxy_packages" => ["openstack-nova-novncproxy"], # me thinks this is right? + "compute_vncproxy_service" => "openstack-nova-novncproxy", + "compute_vncproxy_console_packages" => ["openstack-nova-console"], + "compute_vncproxy_console_service" => "openstack-nova-console", + "compute_vncproxy_console_process_name" => "nova-console", + "compute_vncproxy_consoleauth_packages" => ["openstack-nova-console"], + "compute_vncproxy_consoleauth_service" => "openstack-nova-consoleauth", + "compute_vncproxy_consoleauth_process_name" => "nova-consoleauth", + "libvirt_packages" => ["libvirt"], + "libvirt_service" => "libvirtd", + "compute_cert_packages" => ["openstack-nova-cert"], + "compute_cert_service" => "openstack-nova-cert", + "mysql_service" => "mysqld", + "common_packages" => ["openstack-nova-common"], + "iscsi_helper" => "ietadm", + "nfs_packages" => ["nfs-utils", "nfs-utils-lib"], + "package_overrides" => "" + } + if platform == "suse" + default["openstack"]["compute"]["platform"]["common_packages"] = ["openstack-nova"] + default["openstack"]["compute"]["platform"]["kvm_packages"] = ["kvm"] + default["openstack"]["compute"]["platform"]["xen_packages"] = ["kernel-xen", "xen", "xen-tools"] + default["openstack"]["compute"]["platform"]["lxc_packages"] = ["lxc"] + default["openstack"]["compute"]["platform"]["nfs_packages"] = ["nfs-utils"] + end +when "ubuntu" + default["openstack"]["compute"]["platform"] = { + "api_ec2_packages" => ["nova-api-ec2"], + "api_ec2_service" => "nova-api-ec2", + "api_os_compute_packages" => ["nova-api-os-compute"], + "api_os_compute_process_name" => "nova-api-os-compute", + "api_os_compute_service" => "nova-api-os-compute", + "memcache_python_packages" => ["python-memcache"], + "neutron_python_packages" => ["python-quantumclient", "python-pyparsing"], + "compute_api_metadata_packages" => ["nova-api-metadata"], + "compute_api_metadata_service" => "nova-api-metadata", + "compute_api_metadata_process_name" => "nova-api-metadata", + "compute_compute_packages" => ["nova-compute"], + "compute_compute_service" => "nova-compute", + "compute_network_packages" => ["iptables", "nova-network"], + "compute_network_service" => "nova-network", + "compute_scheduler_packages" => ["nova-scheduler"], + "compute_scheduler_service" => "nova-scheduler", + "compute_conductor_packages" => ["nova-conductor"], + "compute_conductor_service" => "nova-conductor", + # Websockify is needed due to https://bugs.launchpad.net/ubuntu/+source/nova/+bug/1076442 + "compute_vncproxy_packages" => ["novnc", "websockify", "nova-novncproxy"], + "compute_vncproxy_service" => "nova-novncproxy", + "compute_vncproxy_console_packages" => ["nova-console"], + "compute_vncproxy_console_service" => "nova-console", + "compute_vncproxy_console_process_name" => "nova-console", + "compute_vncproxy_consoleauth_packages" => ["nova-consoleauth"], + "compute_vncproxy_consoleauth_service" => "nova-consoleauth", + "compute_vncproxy_consoleauth_process_name" => "nova-consoleauth", + "libvirt_packages" => ["libvirt-bin"], + "libvirt_service" => "libvirt-bin", + "compute_cert_packages" => ["nova-cert"], + "compute_cert_service" => "nova-cert", + "mysql_service" => "mysql", + "common_packages" => ["nova-common"], + "iscsi_helper" => "tgtadm", + "nfs_packages" => ["nfs-common"], + "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end + +# plugins +default["openstack"]["compute"]["plugins"] = nil diff --git a/chef/cookbooks/openstack-compute/files/default/add_floaters.py b/chef/cookbooks/openstack-compute/files/default/add_floaters.py new file mode 100644 index 0000000..ddd2443 --- /dev/null +++ b/chef/cookbooks/openstack-compute/files/default/add_floaters.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 AT&T Services, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +import subprocess + +import netaddr + +DESCRIPTION = "A `nova-manage floating create` and `quantum net create` wrapper." + + +class FloatingAddress(object): + """ + A simple wrapper class for creating networks. Often + times there are reserved addresses at the start of a + network, nova-manage doesn't account for this. + + TODO(retr0h): This should really be added to nova-manage. + TODO(jaypipes): Instead of subprocess calls, just use the quantumclient + """ + + def __init__(self, args): + self._args = args + + def nova_add_cidr(self, cidr): + """ + Validates the provided cider address, and passes it to nova-manage. + + :param cidr: A string containing a valid CIDR address. + """ + netaddr.IPNetwork(cidr) + self.nova_add_floating(cidr) + + def nova_add_range(self, start, end): + """ + Takes a start and end range, and creates individual host addresses. + + :param start: A string containing the start of the range. + :param end: A string containing the end of the range. + """ + ip_list = list(netaddr.iter_iprange(start, end)) + for ip in ip_list: + self.nova_add_floating(ip) + + def nova_add_floating(self, ip): + cmd = "nova-manage floating create --ip_range={0}".format(ip) + if self._args.pool: + cmd += ' --pool={0}'.format(self._args.pool) + if self._args.interface: + cmd += ' --interface={0}'.format(self._args.interface) + + subprocess.check_call(cmd, shell=True) + + def neutron_add_floating(self, cidr): + + # convert cidr string to IPNetwork object + cidr = netaddr.IPNetwork(cidr) + + # ensure we have a public network and we only ever create one + cmd = "if ! quantum net-show public; then quantum net-create %s -- --router:external=True; fi" % self._args.pool + + try: + subprocess.check_call(cmd, shell=True) + except: + # we failed to query the quanutm api, we'll ignore this error + # and return now so any surrounding chef runs can continue + # since this script may actually be running on the quantum api + print "ERROR: Failed to query the quantum api for the public network" + return + + cmd = "quantum subnet-list -Fcidr -fcsv --quote=none | grep '%s'" % cidr + + res = subprocess.call(cmd, shell=True) + if res == 0: + # Subnet has already been created... + return + + # calculate the start and end values + ip_start = cidr.ip + ip_end = netaddr.IPAddress(cidr.last-1) + + # create a new subnet + cmd = "quantum subnet-create --allocation-pool start=%s,end=%s %s %s -- --enable_dhcp=False" % \ + (ip_start, ip_end, self._args.pool, cidr) + subprocess.check_call(cmd, shell=True) + + +def parse_args(): + ap = argparse.ArgumentParser(description=DESCRIPTION) + subparsers = ap.add_subparsers(help='sub-command help', dest='subparser_name') + + # create the parser for the "nova" command + parser_nova = subparsers.add_parser('nova', help='Use Nova Backend') + parser_nova.add_argument('--pool', + required=True, + help="Name of the floating pool") + parser_nova.add_argument('--interface', + required=False, + help="Network interface to bring the floating " + "addresses up on") + group = parser_nova.add_mutually_exclusive_group(required=True) + group.add_argument('--cidr', + help="A CIDR notation of addresses to add " + "(e.g. 192.168.0.0/24)") + group.add_argument('--ip-range', + help="A range of addresses to add " + "(e.g. 192.168.0.10,192.168.0.50)") + + # create the parser for the "neutron command" + parser_neutron = subparsers.add_parser('neutron', help='Use Neutron Backend') + parser_neutron.add_argument('--cidr', + required=True, + help="A CIDR notation of addresses to add " + "(e.g. 192.168.0.11/24 to start at .11 " + "and end at .254)") + parser_neutron.add_argument('--pool', + required=True, + help="Name of the public network") + return ap.parse_args() + +if __name__ == '__main__': + args = parse_args() + fa = FloatingAddress(args) + + if args.subparser_name == 'nova': + if args.cidr: + fa.nova_add_cidr(args.cidr) + elif args.ip_range: + start, end = args.ip_range.split(',') + fa.nova_add_range(start, end) + + elif args.subparser_name == 'neutron': + fa.neutron_add_floating(args.cidr) diff --git a/chef/cookbooks/openstack-compute/files/default/nova-compute.conf b/chef/cookbooks/openstack-compute/files/default/nova-compute.conf new file mode 100644 index 0000000..099e72c --- /dev/null +++ b/chef/cookbooks/openstack-compute/files/default/nova-compute.conf @@ -0,0 +1,5 @@ +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +# +# P.S. Ubuntu YOUR DOING IT WRONG!! +# diff --git a/chef/cookbooks/openstack-compute/files/default/nova_plugin.py b/chef/cookbooks/openstack-compute/files/default/nova_plugin.py new file mode 100644 index 0000000..3876f19 --- /dev/null +++ b/chef/cookbooks/openstack-compute/files/default/nova_plugin.py @@ -0,0 +1,135 @@ +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from keystoneclient.v2_0 import Client as KeystoneClient +from novaclient.client import Client as NovaClient +from novaclient import exceptions + +import collectd + +global NAME, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING + +NAME = "nova_plugin" +OS_USERNAME = "username" +OS_PASSWORD = "password" +OS_TENANT_NAME = "tenantname" +OS_AUTH_URL = "http://localhost:5000/v2.0" +VERBOSE_LOGGING = False + +def get_stats(user, passwd, tenant, url): + keystone = KeystoneClient(username=user, password=passwd, tenant_name=tenant, auth_url=url) + + # Find my uuid + user_list = keystone.users.list() + admin_uuid = "" + for usr in user_list: + if usr.name == user: + admin_uuid = usr.id + + # Find out which tenants I have roles in + tenant_list = keystone.tenants.list() + my_tenants = list() + for tenant in tenant_list: + if keystone.users.list_roles(user=admin_uuid, tenant=tenant.id): + my_tenants.append( { "name": tenant.name, "id": tenant.id } ) + + #prefix = "openstack.nova.cluster" + prefix = "openstack.nova" + + # Default data structure + data = dict() + + # Prep counters + data["%s.total.count" % (prefix)] = 0 + counters = ('ram', 'vcpus', 'disk', 'ephemeral') + for counter in counters: + data["%s.total.%s" % (prefix,counter)] = 0 + + # for tenant in tenant_list: + for tenant in my_tenants: + client = NovaClient("1.1",user,passwd,tenant['name'],url,service_type="compute") + + # Figure out how much ram has been allocated total for all servers + server_list = client.servers.list() + data["%s.total.count" % (prefix)] += len(server_list) + + data["%s.tenant.%s.count" % (prefix,tenant['name'])] = 0 + + for server in server_list: + flavor = client.flavors.get(int(server.flavor["id"])) + tenant_uuid = keystone.tenants.get(server.tenant_id).name + data["%s.tenant.%s.count" % (prefix,tenant_uuid)] += 1 + for counter in counters: + data["%s.total.%s" % (prefix,counter)] += int(flavor.__getattribute__(counter)) + if "%s.%s.%s" % (prefix,tenant_uuid, counter) in data: + data["%s.tenant.%s.%s" % (prefix,tenant_uuid,counter)] += int(flavor.__getattribute__(counter)) + else: + data["%s.tenant.%s.%s" % (prefix,tenant_uuid,counter)] = int(flavor.__getattribute__(counter)) + + ########## + # debug + for key in data.keys(): + print "%s = %s" % (key, data[key]) + ########## + + return data + + +def configure_callback(conf): + """Received configuration information""" + global OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING + for node in conf.children: + if node.key == "Username": + OS_USERNAME = node.values[0] + elif node.key == "Password": + OS_PASSWORD = node.values[0] + elif node.key == "TenantName": + OS_TENANT_NAME = node.values[0] + elif node.key == "AuthURL": + OS_AUTH_URL = node.values[0] + elif node.key == "Verbose": + VERBOSE_LOGGING = node.values[0] + else: + logger("warn", "Unknown config key: %s" % node.key) + + +def read_callback(): + logger("verb", "read_callback") + info = get_stats(OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL) + + if not info: + logger("err", "No information received") + return + + for key in info.keys(): + logger('verb', 'Dispatching %s : %i' % (key, int(info[key]))) + val = collectd.Values(plugin=key) + val.type = 'gauge' + val.values = [int(info[key])] + val.dispatch() + + +def logger(t, msg): + if t == 'err': + collectd.error('%s: %s' % (NAME, msg)) + if t == 'warn': + collectd.warning('%s: %s' % (NAME, msg)) + elif t == 'verb' and VERBOSE_LOGGING == True: + collectd.info('%s: %s' % (NAME, msg)) + +collectd.register_config(configure_callback) +collectd.warning("Initializing nova plugin") +collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-compute/files/default/policy.json b/chef/cookbooks/openstack-compute/files/default/policy.json new file mode 100644 index 0000000..373c568 --- /dev/null +++ b/chef/cookbooks/openstack-compute/files/default/policy.json @@ -0,0 +1,3 @@ +{ + "context_is_admin": [["role:admin"]] +} diff --git a/chef/cookbooks/openstack-compute/metadata.rb b/chef/cookbooks/openstack-compute/metadata.rb new file mode 100644 index 0000000..25abb68 --- /dev/null +++ b/chef/cookbooks/openstack-compute/metadata.rb @@ -0,0 +1,33 @@ +name "openstack-compute" +maintainer "Opscode, Inc." +maintainer_email "matt@opscode.com" +license "Apache 2.0" +description "The OpenStack Compute service Nova." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.0" + +# recipe "openstack-compute::api-ec2", "Installs AWS EC2 compatible API" +# recipe "openstack-compute::api-metadata", "Installs the nova metadata package" +# recipe "openstack-compute::api-os-compute", "Installs OS API" +recipe "openstack-compute::compute", "nova-compute service" +recipe "openstack-compute::libvirt", "Installs libvirt, used by nova compute for management of the virtual machine environment" +# recipe "openstack-compute::identity_registration", "Registers the API and EC2 endpoints with Keystone" +# recipe "openstack-compute::network", "Installs nova network service" +# recipe "openstack-compute::nova-cert", "Installs nova-cert service" +recipe "openstack-compute::nova-common", "Builds the basic nova.conf config file with details of the rabbitmq, mysql, glance and keystone servers" +recipe "openstack-compute::nova-setup", "Sets up the nova database on the mysql server, including the initial schema and subsequent creation of the appropriate networks" +#recipe "openstack-compute::scheduler", "Installs nova scheduler service" +#recipe "openstack-compute::vncproxy", "Installs and configures the vncproxy service for console access to VMs" + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "openstack-common", "~> 0.4.0" +depends "openstack-identity", "~> 7.0.0" +depends "openstack-image", "~> 7.0.0" +depends "openstack-network", "~> 7.0.0" +depends "selinux" +depends "sysctl" +depends "yum" +depends "python" diff --git a/chef/cookbooks/openstack-compute/recipes/api-ec2.rb b/chef/cookbooks/openstack-compute/recipes/api-ec2.rb new file mode 100644 index 0000000..c792e5c --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/api-ec2.rb @@ -0,0 +1,87 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: api-ec2 +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +directory "/var/lock/nova" do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 + + action :create +end + +package "python-keystone" do + action :upgrade +end + +platform_options["api_ec2_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +service "nova-api-ec2" do + service_name platform_options["api_ec2_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action :enable +end + +identity_endpoint = endpoint "identity-api" +identity_admin_endpoint = endpoint "identity-admin" +service_tenant_name = node['openstack']['identity']['compute']['tenant'] +service_user = node['openstack']['identity']['compute']['username'] +service_pass = service_password node['openstack']['identity']['compute']['password'] + +#TODO(jaypipes): Move this logic and stuff into the openstack-common +# library cookbook. +auth_uri = identity_endpoint.to_s +if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" + # The auth_uri should contain /v2.0 in most cases, but if the + # auth_version is v3.0, we leave it off. This is only necessary + # for environments that need to support V3 non-default-domain + # tokens, which is really the only reason to set version to + # something other than v2.0 (the default) + auth_uri = auth_uri.gsub('/v2.0', '') +end + +template "/etc/nova/api-paste.ini" do + source "api-paste.ini.erb" + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00644 + variables( + :auth_uri => auth_uri, + :identity_admin_endpoint => identity_admin_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + notifies :restart, "service[nova-api-ec2]" +end diff --git a/chef/cookbooks/openstack-compute/recipes/api-metadata.rb b/chef/cookbooks/openstack-compute/recipes/api-metadata.rb new file mode 100644 index 0000000..7733f22 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/api-metadata.rb @@ -0,0 +1,90 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: api-metadata +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +directory "/var/lock/nova" do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 + + action :create +end + +package "python-keystone" do + action :upgrade +end + +platform_options["compute_api_metadata_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +service "nova-api-metadata" do + service_name platform_options["compute_api_metadata_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action :enable +end + +identity_endpoint = endpoint "identity-api" +identity_admin_endpoint = endpoint "identity-admin" +service_tenant_name = node['openstack']['identity']['compute']['tenant'] +service_user = node['openstack']['identity']['compute']['username'] +service_pass = service_password node['openstack']['identity']['compute']['password'] + +#TODO(jaypipes): Move this logic and stuff into the openstack-common +# library cookbook. +auth_uri = identity_endpoint.to_s +if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" + # The auth_uri should contain /v2.0 in most cases, but if the + # auth_version is v3.0, we leave it off. This is only necessary + # for environments that need to support V3 non-default-domain + # tokens, which is really the only reason to set version to + # something other than v2.0 (the default) + auth_uri = auth_uri.gsub('/v2.0', '') +end + +template "/etc/nova/api-paste.ini" do + source "api-paste.ini.erb" + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00644 + variables( + :auth_uri => auth_uri, + :identity_admin_endpoint => identity_admin_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + notifies :restart, "service[nova-api-metadata]" +end diff --git a/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb b/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb new file mode 100644 index 0000000..abc1338 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/api-os-compute.rb @@ -0,0 +1,91 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: api-os-compute +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +directory "/var/lock/nova" do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 +end + +directory ::File.dirname(node["openstack"]["compute"]["api"]["auth"]["cache_dir"]) do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 +end + +package "python-keystone" do + action :upgrade +end + +platform_options["api_os_compute_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +service "nova-api-os-compute" do + service_name platform_options["api_os_compute_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action [:enable, :start] +end + +identity_endpoint = endpoint "identity-api" +identity_admin_endpoint = endpoint "identity-admin" +service_tenant_name = node['openstack']['identity']['compute']['tenant'] +service_user = node['openstack']['identity']['compute']['username'] +service_pass = service_password node['openstack']['identity']['compute']['password'] + +#TODO(jaypipes): Move this logic and stuff into the openstack-common +# library cookbook. +auth_uri = identity_endpoint.to_s +if node["openstack"]["compute"]["api"]["auth"]["version"] != "v2.0" + # The auth_uri should contain /v2.0 in most cases, but if the + # auth_version is v3.0, we leave it off. This is only necessary + # for environments that need to support V3 non-default-domain + # tokens, which is really the only reason to set version to + # something other than v2.0 (the default) + auth_uri = auth_uri.gsub('/v2.0', '') +end + +template "/etc/nova/api-paste.ini" do + source "api-paste.ini.erb" + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00644 + variables( + :auth_uri => auth_uri, + :identity_admin_endpoint => identity_admin_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + notifies :restart, "service[nova-api-os-compute]" +end diff --git a/chef/cookbooks/openstack-compute/recipes/compute.rb b/chef/cookbooks/openstack-compute/recipes/compute.rb new file mode 100644 index 0000000..f37a0dd --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/compute.rb @@ -0,0 +1,86 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: compute +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-compute::nova-common" +# include_recipe "openstack-compute::api-metadata" +unless node.run_list.include? "openstack-network::server" + include_recipe "openstack-compute::network" +end + +platform_options = node["openstack"]["compute"]["platform"] +# Note(maoy): Make sure compute_compute_packages is not a node object. +# so that this is compatible with chef 11 when being changed later. +compute_compute_packages = Array.new(platform_options["compute_compute_packages"]) + +if platform?(%w(ubuntu)) + if node["openstack"]["compute"]["libvirt"]["virt_type"] == "kvm" + compute_compute_packages << "nova-compute-kvm" + elsif node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" + compute_compute_packages << "nova-compute-qemu" + end +end + +if platform?(%w(centos, redhat, fedora)) + if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" + compute_compute_packages << "qemu-kvm" + elsif node["openstack"]["compute"]["libvirt"]["virt_type"] == "kvm" + compute_compute_packages << "qemu-kvm" + end +end + +compute_compute_packages.each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +# Installing nfs client packages because in grizzly, cinder nfs is supported +# Never had to install iscsi packages because nova-compute package depends it +# So volume-attach 'just worked' before - alop +platform_options["nfs_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +cookbook_file "/etc/nova/nova-compute.conf" do + source "nova-compute.conf" + mode 00644 + retries 5 + action :create +end + +service "nova-compute" do + service_name platform_options["compute_compute_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action [ :enable, :restart ] +end + +include_recipe "openstack-compute::libvirt" diff --git a/chef/cookbooks/openstack-compute/recipes/conductor.rb b/chef/cookbooks/openstack-compute/recipes/conductor.rb new file mode 100644 index 0000000..2a208bc --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/conductor.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: nova +# Recipe:: conductor +# +# Copyright 2012, Rackspace US, Inc. +# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +platform_options["compute_conductor_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :upgrade + end +end + +service "nova-conductor" do + service_name platform_options["compute_conductor_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + action [ :enable, :restart ] +end diff --git a/chef/cookbooks/openstack-compute/recipes/default.rb b/chef/cookbooks/openstack-compute/recipes/default.rb new file mode 100644 index 0000000..6fa23bb --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/openstack-compute/recipes/identity_registration.rb b/chef/cookbooks/openstack-compute/recipes/identity_registration.rb new file mode 100644 index 0000000..64edf82 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/identity_registration.rb @@ -0,0 +1,116 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: identity_registration +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" +bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +auth_uri = ::URI.decode identity_admin_endpoint.to_s +service_pass = service_password node['openstack']['identity']['compute']['password'] +service_user = node['openstack']['identity']['compute']['username'] +service_role = node['openstack']['identity']['compute']['role'] +service_tenant_name = node['openstack']['identity']['compute']['tenant'] +nova_api_endpoint = endpoint "compute-api" +ec2_admin_endpoint = endpoint "compute-ec2-admin" +ec2_public_endpoint = endpoint "compute-ec2-api" +region = node["openstack"]["compute"]["region"] + +# Register Service Tenant +openstack_identity_register "Register Service Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + tenant_description "Service Tenant" + + action :create_tenant +end + +# Register Service User +openstack_identity_register "Register Service User" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + + action :create_user +end + +## Grant Admin role to Service User for Service Tenant ## +openstack_identity_register "Grant 'admin' Role to Service User for Service Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end + +# Register Compute Service +openstack_identity_register "Register Compute Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "nova" + service_type "compute" + service_description "Nova Compute Service" + + action :create_service +end + +# Register Compute Endpoint +openstack_identity_register "Register Compute Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type "compute" + endpoint_region region + endpoint_adminurl ::URI.decode nova_api_endpoint.to_s + endpoint_internalurl ::URI.decode nova_api_endpoint.to_s + endpoint_publicurl ::URI.decode nova_api_endpoint.to_s + + action :create_endpoint +end + +# Register EC2 Service +openstack_identity_register "Register EC2 Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "ec2" + service_type "ec2" + service_description "EC2 Compatibility Layer" + + action :create_service +end + +# Register EC2 Endpoint +openstack_identity_register "Register EC2 Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type "ec2" + endpoint_region region + endpoint_adminurl ::URI.decode ec2_admin_endpoint.to_s + endpoint_internalurl ::URI.decode ec2_public_endpoint.to_s + endpoint_publicurl ::URI.decode ec2_public_endpoint.to_s + + action :create_endpoint +end diff --git a/chef/cookbooks/openstack-compute/recipes/libvirt.rb b/chef/cookbooks/openstack-compute/recipes/libvirt.rb new file mode 100644 index 0000000..86d7361 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/libvirt.rb @@ -0,0 +1,205 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: libvirt +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +platform_options = node["openstack"]["compute"]["platform"] + +platform_options["libvirt_packages"].each do |pkg| + package pkg do + action :install + end +end + +def set_grub_default_kernel(flavor='default') + default_boot, current_default = 0, nil + + # parse menu.lst, to find boot index for selected flavor + File.open('/boot/grub/menu.lst') do |f| + f.lines.each do |line| + current_default = line.scan(/\d/).first.to_i if line.start_with?('default') + + if line.start_with?('title') + if flavor.eql?('xen') + # found boot index + break if line.include?('Xen') + else + # take first kernel as default, unless we are searching for xen + # kernel + break + end + default_boot += 1 + end + end + end + + # change default option for /boot/grub/menu.lst + unless current_default.eql?(default_boot) + ::Chef::Log.info("Changed grub default to #{default_boot}") + %x[sed -i -e "s;^default.*;default #{default_boot};" /boot/grub/menu.lst] + end +end + +def set_grub2_default_kernel(flavor='default') + boot_entry = "'openSUSE GNU/Linux, with Xen hypervisor'" + if system("grub2-set-default #{boot_entry}") + ::Chef::Log.info("Changed grub2 default to #{boot_entry}") + else + ::Chef::Application.fatal!( + "Unable to change grub2 default to #{boot_entry}") + end +end + +def set_boot_kernel_and_trigger_reboot(flavor='default') + # only default and xen flavor is supported by this helper right now + if File.exists?("/boot/grub/menu.lst") + set_grub_default_kernel(flavor) + elsif File.exists?("/etc/default/grub") + set_grub2_default_kernel(flavor) + else + ::Chef::Application.fatal!( + "Unknown bootloader. Could not change boot kernel.") + end + + # trigger reboot through reboot_handler, if kernel-$flavor is not yet + # running + unless %x[uname -r].include?(flavor) + node.run_state["reboot"] = true + end +end + +# on suse nova-compute don't depends on any virtualization mechanism +case node["platform"] +when "suse" + case node["openstack"]["compute"]["libvirt"]["virt_type"] + when "kvm" + node["openstack"]["compute"]["platform"]["kvm_packages"].each do |pkg| + package pkg do + action :install + end + end + execute "loading kvm modules" do + command "grep -q vmx /proc/cpuinfo && /sbin/modprobe kvm-intel; grep -q svm /proc/cpuinfo && /sbin/modprobe kvm-amd; /sbin/modprobe vhost-net" + end + # NOTE(saschpe): Allow switching from XEN to KVM: + set_boot_kernel_and_trigger_reboot + + when "xen" + node["openstack"]["compute"]["platform"]["xen_packages"].each do |pkg| + package pkg do + action :install + end + end + set_boot_kernel_and_trigger_reboot('xen') + + when "qemu" + node["openstack"]["compute"]["platform"]["kvm_packages"].each do |pkg| + package pkg do + action :install + end + end + + when "lxc" + node["openstack"]["compute"]["platform"]["lxc_packages"].each do |pkg| + package pkg do + action :install + end + end + service "boot.cgroup" do + action [:enable, :start] + end + end +end + +group node["openstack"]["compute"]["libvirt"]["group"] do + append true + members [node["openstack"]["compute"]["group"]] + + action :create + only_if { platform? %w{suse fedora redhat centos} } +end + +# http://fedoraproject.org/wiki/Getting_started_with_OpenStack_EPEL#Installing_within_a_VM +# ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64 +link "/usr/bin/qemu-system-x86_64" do + to "/usr/libexec/qemu-kvm" + + only_if { platform? %w{fedora redhat centos} } +end + +if node['platform_family'] != "rhel" + service "dbus" do + action [:enable, :start] + end +end + +service "libvirt-bin" do + service_name platform_options["libvirt_service"] + supports :status => true, :restart => true + + action [:enable, :start] +end + +execute "Disabling default libvirt network" do + command "virsh net-autostart default --disable" + + only_if "virsh net-list | grep -q default" +end + +execute "Deleting default libvirt network" do + command "virsh net-destroy default" + + only_if "virsh net-list | grep -q default" +end + +# TODO(breu): this section needs to be rewritten to support key privisioning +template "/etc/libvirt/libvirtd.conf" do + source "libvirtd.conf.erb" + owner "root" + group "root" + mode 00644 + variables( + :auth_tcp => node["openstack"]["compute"]["libvirt"]["auth_tcp"], + :libvirt_group => node["openstack"]["compute"]["libvirt"]["group"] + ) + + notifies :restart, "service[libvirt-bin]", :immediately + not_if { platform? "suse" } +end + +template "/etc/default/libvirt-bin" do + source "libvirt-bin.erb" + owner "root" + group "root" + mode 00644 + + notifies :restart, "service[libvirt-bin]", :immediately + + only_if { platform? %w{ubuntu debian} } +end + +template "/etc/sysconfig/libvirtd" do + source "libvirtd.erb" + owner "root" + group "root" + mode 00644 + + notifies :restart, "service[libvirt-bin]", :immediately + + only_if { platform? %w{fedora redhat centos} } +end diff --git a/chef/cookbooks/openstack-compute/recipes/network.rb b/chef/cookbooks/openstack-compute/recipes/network.rb new file mode 100644 index 0000000..b94822b --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/network.rb @@ -0,0 +1,52 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: network +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +# the only type of network we process here is nova, otherwise for +# quantum, the network will be setup by the inclusion of +# openstack-network recipes + +if node["openstack"]["compute"]["network"]["service_type"] == "nova" + + platform_options["compute_network_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end + end + + service "nova-network" do + service_name platform_options["compute_network_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + action :enable + end + +else + + node["openstack"]["compute"]["network"]["plugins"].each do |plugin| + include_recipe "openstack-network::#{plugin}" + end + +end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-cert.rb b/chef/cookbooks/openstack-compute/recipes/nova-cert.rb new file mode 100644 index 0000000..6aec24d --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/nova-cert.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: nova-cert +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "openstack-compute::nova-common" + +platform_options=node["openstack"]["compute"]["platform"] + +platform_options["compute_cert_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +service "nova-cert" do + service_name platform_options["compute_cert_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action [ :enable, :restart ] +end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-common.rb b/chef/cookbooks/openstack-compute/recipes/nova-common.rb new file mode 100644 index 0000000..f8fc49e --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/nova-common.rb @@ -0,0 +1,217 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: nova-common +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +if platform?(%w(fedora redhat centos)) # :pragma-foodcritic: ~FC024 - won't fix this + include_recipe "yum::epel" +end +if node["openstack"]["compute"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options = node["openstack"]["compute"]["platform"] + +platform_options["common_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +# required to run more than one consoleauth process +platform_options["memcache_python_packages"].each do |pkg| + package pkg do + action :install + end +end + +directory "/etc/nova" do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 + + action :create +end + +directory "/etc/nova/rootwrap.d" do + # Must be root! + owner "root" + group "root" + mode 00700 + + action :create +end + +db_user = node['openstack']['db']['compute']['username'] +db_pass = db_password node['openstack']['db']['compute']['password'] +sql_connection = db_uri("compute", db_user, db_pass) + +if node["openstack"]["compute"]["rabbit"]["ha"] + rabbit_hosts = node['openstack']['mq']['bind_address'] +end +rabbit_pass = user_password node['openstack']['mq']['password'] + +identity_service_role = node["openstack"]["compute"]["identity_service_chef_role"] + +if node.run_list.roles.include?(identity_service_role) + # if role is on this node, just return the node hash + keystone = node +else + keystone = node + # otherwise go searching + # keystone = search_for(identity_service_role).first +end + +ksadmin_tenant_name = keystone["openstack"]["identity"]["admin_tenant_name"] +ksadmin_user = keystone["openstack"]["identity"]["admin_user"] +ksadmin_pass = user_password node['openstack']['identity']['admin_password'] + +memcache_servers = memcached_servers.join "," + +# find the node attribute endpoint settings for the server holding a given role +identity_endpoint = endpoint "identity-api" +xvpvnc_endpoint = endpoint "compute-xvpvnc" || {} +novnc_endpoint = endpoint "compute-novnc" || {} +compute_api_endpoint = endpoint "compute-api" || {} +ec2_public_endpoint = endpoint "compute-ec2-api" || {} +network_endpoint = endpoint "network-api" || {} +image_endpoint = endpoint "image-api" + +Chef::Log.debug("openstack-compute::nova-common:keystone|#{keystone}") +Chef::Log.debug("openstack-compute::nova-common:ksadmin_user|#{ksadmin_user}") +Chef::Log.debug("openstack-compute::nova-common:ksadmin_tenant_name|#{ksadmin_tenant_name}") +Chef::Log.debug("openstack-compute::nova-common:identity_endpoint|#{identity_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:xvpvnc_endpoint|#{xvpvnc_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:novnc_endpoint|#{novnc_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:compute_api_endpoint|#{::URI.decode compute_api_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:ec2_public_endpoint|#{ec2_public_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:network_endpoint|#{network_endpoint.to_s}") +Chef::Log.debug("openstack-compute::nova-common:image_endpoint|#{image_endpoint.to_s}") + +vnc_bind_ip = address_for node['openstack']['networking']['control']['interface'] +xvpvnc_proxy_ip = address_for node["openstack"]["compute"]["xvpvnc_proxy"]["bind_interface"] +novnc_proxy_ip = address_for node["openstack"]["compute"]["novnc_proxy"]["bind_interface"] + +if node["openstack"]["compute"]["network"]["service_type"] == "quantum" + quantum_admin_password = service_password node['openstack']['identity']['network']['password'] + quantum_metadata_proxy_shared_secret = secret "secrets", node["openstack"]["network"]["metadata"]["secret_name"] +else + quantum_admin_password = nil + quantum_metadata_proxy_shared_secret = nil +end + +# virtualization: '0' hardware support kvm, '1' hardware doesn't +virtualization = `egrep '(vmx|svm)' --color=always /proc/cpuinfo >/dev/null;echo $?`.delete("\n") +if virtualization.eql?("1") + node.override["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" +else + node.override["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" +end + +template "/etc/nova/nova.conf" do + source "nova.conf.erb" + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection, + :novncproxy_base_url => novnc_endpoint.to_s, + :xvpvncproxy_base_url => xvpvnc_endpoint.to_s, + :xvpvncproxy_bind_host => xvpvnc_proxy_ip, + :novncproxy_bind_host => novnc_proxy_ip, + :vncserver_listen => vnc_bind_ip, + :vncserver_proxyclient_address => vnc_bind_ip, + :memcache_servers => memcache_servers, + :rabbit_password => rabbit_pass, + :rabbit_hosts => rabbit_hosts, + :identity_endpoint => identity_endpoint, + # TODO(jaypipes): No support here for >1 image API servers + # with the glance_api_servers configuration option... + :glance_api_ipaddress => image_endpoint.host, + :glance_api_port => image_endpoint.port, + :iscsi_helper => platform_options["iscsi_helper"], + :scheduler_default_filters => node["openstack"]["compute"]["scheduler"]["default_filters"].join(","), + :osapi_compute_link_prefix => compute_api_endpoint.to_s, + :network_endpoint => network_endpoint, + :quantum_admin_password => quantum_admin_password, + :quantum_metadata_proxy_shared_secret => quantum_metadata_proxy_shared_secret + ) +end + +template "/etc/nova/rootwrap.conf" do + source "rootwrap.conf.erb" + # Must be root! + owner "root" + group "root" + mode 00644 +end + +template "/etc/nova/rootwrap.d/api-metadata.filters" do + source "rootwrap.d/api-metadata.filters.erb" + # Must be root! + owner "root" + group "root" + mode 00644 +end + +template "/etc/nova/rootwrap.d/compute.filters" do + source "rootwrap.d/compute.filters.erb" + # Must be root! + owner "root" + group "root" + mode 00644 +end + +template "/etc/nova/rootwrap.d/network.filters" do + source "rootwrap.d/network.filters.erb" + # Must be root! + owner "root" + group "root" + mode 00644 +end + +# TODO: need to re-evaluate this for accuracy +# TODO(jaypipes): This should be moved into openstack-common +# and evaluated only on nodes with admin privs. +template "/root/openrc" do + source "openrc.erb" + # Must be root! + owner "root" + group "root" + mode 00600 + variables( + :user => ksadmin_user, + :tenant => ksadmin_tenant_name, + :password => ksadmin_pass, + :identity_endpoint => identity_endpoint, + :auth_strategy => "keystone", + :ec2_url => ec2_public_endpoint.to_s + ) +end + +execute "enable nova login" do + command "usermod -s /bin/sh #{node["openstack"]["compute"]["user"]}" +end diff --git a/chef/cookbooks/openstack-compute/recipes/nova-setup.rb b/chef/cookbooks/openstack-compute/recipes/nova-setup.rb new file mode 100644 index 0000000..8bc3d12 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/nova-setup.rb @@ -0,0 +1,134 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: nova-setup +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-compute::nova-common" + +execute "nova-manage db sync" do + command "nova-manage db sync" + + action :run +end + +case node["openstack"]["compute"]["network"]["service_type"] +when "nova" + + next_vlan = 100 + node["openstack"]["compute"]["networks"].each do |net| + execute "nova-manage network create --label=#{net['label']}" do + # The only two required keys in each network Hash + # are "label" and "ipv4_cidr". + cmd = "nova-manage network create --label=#{net['label']} --fixed_range_v4=#{net['ipv4_cidr']}" + if net.has_key?("multi_host") + cmd += " --multi_host='#{net['multi_host']}'" + end + if net.has_key?("num_networks") + cmd += " --num_networks=#{net['num_networks']}" + end + if net.has_key?("network_size") + cmd += " --network_size=#{net['network_size']}" + end + if net.has_key?("bridge") + cmd += " --bridge=#{net['bridge']}" + end + # Older attributes have the key as "bridge_dev" instead + # of "bridge_interface"... + if net.has_key?("bridge_interface") or net.has_key?("bridge_dev") + val = net.has_key?("bridge_interface") ? net["bridge_interface"] : net["bridge_dev"] + cmd += " --bridge_interface=#{val}" + end + if net.has_key?("dns1") + cmd += " --dns1=#{net['dns1']}" + end + if net.has_key?("dns2") + cmd += " --dns2=#{net['dns2']}" + end + if net.has_key?("vlan") + cmd += " --vlan=#{net['vlan']}" + elsif node["openstack"]["compute"]["network"]["network_manager"] == "nova.network.manager.VlanManager" + cmd += " --vlan=#{next_vlan}" + next_vlan = next_vlan + 1 + end + + command cmd + not_if "nova-manage network list | grep #{net['ipv4_cidr']}" + + action :run + end + end + + cookbook_file node["openstack"]["compute"]["floating_cmd"] do + source "add_floaters.py" + mode 00755 + + action :create + end + + floating = node["openstack"]["compute"]["network"]["floating"] + if floating && (floating["ipv4_cidr"] || floating["ipv4_range"]) + cmd = "" + if floating["ipv4_cidr"] + cmd = "#{node["openstack"]["compute"]["floating_cmd"]} nova --cidr=#{floating["ipv4_cidr"]}" + elsif floating["ipv4_range"] + cmd = "#{node["openstack"]["compute"]["floating_cmd"]} nova --ip-range=#{floating["ipv4_range"]}" + end + + execute "nova-manage floating create" do + command cmd + + not_if "nova-manage floating list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'" + + action :run + end + end + +when "quantum", "neutron" + + platform_options = node["openstack"]["compute"]["platform"] + + platform_options["neutron_python_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :upgrade + end + end + + cookbook_file node["openstack"]["compute"]["floating_cmd"] do + source "add_floaters.py" + mode 00755 + + action :create + end + + floating = node["openstack"]["compute"]["network"]["floating"] + if floating && floating["ipv4_cidr"] + cmd = ". /root/openrc && #{node["openstack"]["compute"]["floating_cmd"]} neutron --cidr=#{floating["ipv4_cidr"]} --pool=#{floating["public_network_name"]}" + + execute "quantum floating create" do + command cmd + not_if ". /root/openrc && quantum floatingip-list |grep -E '.*([0-9]{1,3}[\.]){3}[0-9]{1,3}*'" + only_if { File.exists?("/root/openrc") } + + action :run + end + end +end diff --git a/chef/cookbooks/openstack-compute/recipes/scheduler.rb b/chef/cookbooks/openstack-compute/recipes/scheduler.rb new file mode 100644 index 0000000..ac09482 --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/scheduler.rb @@ -0,0 +1,47 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: scheduler +# Copyright 2013, Craig Tracey +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +directory "/var/lock/nova" do + owner node["openstack"]["compute"]["user"] + group node["openstack"]["compute"]["group"] + mode 00700 + + action :create +end + +platform_options["compute_scheduler_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +service "nova-scheduler" do + service_name platform_options["compute_scheduler_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action [ :enable, :restart ] +end diff --git a/chef/cookbooks/openstack-compute/recipes/vncproxy.rb b/chef/cookbooks/openstack-compute/recipes/vncproxy.rb new file mode 100644 index 0000000..9ea8a7f --- /dev/null +++ b/chef/cookbooks/openstack-compute/recipes/vncproxy.rb @@ -0,0 +1,70 @@ +# +# Cookbook Name:: openstack-compute +# Recipe:: vncproxy +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-compute::nova-common" + +platform_options = node["openstack"]["compute"]["platform"] + +platform_options["compute_vncproxy_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + + action :upgrade + end +end + +# required for vnc console authentication +platform_options["compute_vncproxy_console_packages"].each do |pkg| + package pkg do + action :upgrade + only_if { platform?("ubuntu") or platform?(%w(fedora redhat centos))} + end +end + +platform_options["compute_vncproxy_consoleauth_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +proxy_service = platform_options["compute_vncproxy_service"] + +service proxy_service do + service_name proxy_service + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action :enable +end + +service "nova-console" do + service_name platform_options["compute_vncproxy_console_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + action [:enable, :start] +end + +service "nova-consoleauth" do + service_name platform_options["compute_vncproxy_consoleauth_service"] + supports :status => true, :restart => true + subscribes :restart, resources("template[/etc/nova/nova.conf]") + + action [:enable, :start] +end diff --git a/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb new file mode 100644 index 0000000..4e4606c --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-ec2-redhat_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-ec2" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::api-ec2" + end + + it "installs ec2 api packages" do + expect(@chef_run).to upgrade_package "openstack-nova-api" + end + + it "starts ec2 api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb b/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb new file mode 100644 index 0000000..2681ece --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-ec2_spec.rb @@ -0,0 +1,27 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-ec2" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::api-ec2" + end + + expect_runs_nova_common_recipe + + expect_creates_nova_lock_dir + + expect_installs_python_keystone + + it "installs ec2 api packages" do + expect(@chef_run).to upgrade_package "nova-api-ec2" + end + + it "starts ec2 api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-api-ec2" + end + + expect_creates_api_paste "service[nova-api-ec2]" + end +end diff --git a/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb new file mode 100644 index 0000000..b00130a --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-metadata-redhat_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-metadata" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::api-metadata" + end + + it "installs metadata api packages" do + expect(@chef_run).to upgrade_package "openstack-nova-api" + end + + it "starts metadata api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb b/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb new file mode 100644 index 0000000..8b0cc84 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-metadata_spec.rb @@ -0,0 +1,27 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-metadata" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::api-metadata" + end + + expect_runs_nova_common_recipe + + expect_creates_nova_lock_dir + + expect_installs_python_keystone + + it "installs metadata api packages" do + expect(@chef_run).to upgrade_package "nova-api-metadata" + end + + it "starts metadata api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-api-metadata" + end + + expect_creates_api_paste "service[nova-api-metadata]" + end +end diff --git a/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb new file mode 100644 index 0000000..b957c5c --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-os-compute-redhat_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-os-compute" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::api-os-compute" + end + + it "installs openstack api packages" do + expect(@chef_run).to upgrade_package "openstack-nova-api" + end + + it "starts openstack api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-api" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb b/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb new file mode 100644 index 0000000..d2b828f --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/api-os-compute_spec.rb @@ -0,0 +1,45 @@ +require_relative "spec_helper" + +describe "openstack-compute::api-os-compute" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::api-os-compute" + end + + expect_runs_nova_common_recipe + + expect_creates_nova_lock_dir + + describe "/var/cache/nova" do + before do + @dir = @chef_run.directory "/var/cache/nova" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "nova", "nova" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + expect_installs_python_keystone + + it "installs openstack api packages" do + expect(@chef_run).to upgrade_package "nova-api-os-compute" + end + + it "starts openstack api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-api-os-compute" + end + + it "starts openstack api now" do + expect(@chef_run).to start_service "nova-api-os-compute" + end + + expect_creates_api_paste "service[nova-api-os-compute]" + end +end diff --git a/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb b/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb new file mode 100644 index 0000000..fa4c1cb --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/compute-opensuse_spec.rb @@ -0,0 +1,16 @@ +require_relative "spec_helper" + +describe "openstack-compute::compute" do + before { compute_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-compute::compute" + end + + it "installs nfs client packages" do + expect(@chef_run).to upgrade_package "nfs-utils" + expect(@chef_run).not_to upgrade_package "nfs-utils-lib" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb new file mode 100644 index 0000000..48b4e91 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/compute-redhat_spec.rb @@ -0,0 +1,45 @@ +require_relative "spec_helper" + +describe "openstack-compute::compute" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::compute" + end + + it "does not install kvm when virt_type is 'kvm'" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" + chef_run.converge "openstack-compute::compute" + expect(chef_run).to_not upgrade_package "nova-compute-kvm" + end + + it "does not install qemu when virt_type is 'qemu'" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" + chef_run.converge "openstack-compute::compute" + expect(chef_run).to_not upgrade_package "nova-compute-qemu" + end + + it "installs nova compute packages" do + expect(@chef_run).to upgrade_package "openstack-nova-compute" + end + + it "installs nfs client packages" do + expect(@chef_run).to upgrade_package "nfs-utils" + expect(@chef_run).to upgrade_package "nfs-utils-lib" + end + + it "starts nova compute on boot" do + expected = "openstack-nova-compute" + expect(@chef_run).to set_service_to_start_on_boot expected + end + + it "starts nova compute" do + expect(@chef_run).to start_service "openstack-nova-compute" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/compute_spec.rb b/chef/cookbooks/openstack-compute/spec/compute_spec.rb new file mode 100644 index 0000000..42d14a6 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/compute_spec.rb @@ -0,0 +1,84 @@ +require_relative "spec_helper" + +describe "openstack-compute::compute" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::compute" + end + + expect_runs_nova_common_recipe + + it "runs api-metadata recipe" do + expect(@chef_run).to include_recipe "openstack-compute::api-metadata" + end + + it "runs network recipe" do + expect(@chef_run).to include_recipe "openstack-compute::network" + end + + it "doesn't run network recipe with openstack-network::server" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.run_list.stub("include?").and_return true + chef_run.converge "openstack-compute::compute" + + expect(chef_run).not_to include_recipe "openstack-compute::network" + end + + it "installs nova compute packages" do + expect(@chef_run).to upgrade_package "nova-compute" + end + + it "installs nfs client packages" do + expect(@chef_run).to upgrade_package "nfs-common" + end + + it "installs kvm when virt_type is 'kvm'" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "kvm" + chef_run.converge "openstack-compute::compute" + + expect(chef_run).to upgrade_package "nova-compute-kvm" + expect(chef_run).not_to upgrade_package "nova-compute-qemu" + end + + it "installs qemu when virt_type is 'qemu'" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" + chef_run.converge "openstack-compute::compute" + + expect(chef_run).to upgrade_package "nova-compute-qemu" + expect(chef_run).not_to upgrade_package "nova-compute-kvm" + end + + describe "nova-compute.conf" do + before do + @file = @chef_run.cookbook_file "/etc/nova/nova-compute.conf" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + it "starts nova compute on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-compute" + end + + it "starts nova compute" do + expect(@chef_run).to start_service "nova-compute" + end + + it "runs libvirt recipe" do + expect(@chef_run).to include_recipe "openstack-compute::libvirt" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb new file mode 100644 index 0000000..8b2bbe6 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/conductor_redhat_spec.rb @@ -0,0 +1,25 @@ +require_relative "spec_helper" + +describe "openstack-compute::conductor" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::conductor" + end + + expect_runs_nova_common_recipe + + it "installs conductor packages" do + expect(@chef_run).to upgrade_package "openstack-nova-conductor" + end + + it "starts nova-conductor on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-conductor" + end + + it "starts nova-conductor" do + expect(@chef_run).to start_service "openstack-nova-conductor" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/conductor_spec.rb b/chef/cookbooks/openstack-compute/spec/conductor_spec.rb new file mode 100644 index 0000000..894b7be --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/conductor_spec.rb @@ -0,0 +1,25 @@ +require_relative "spec_helper" + +describe "openstack-compute::conductor" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::conductor" + end + + expect_runs_nova_common_recipe + + it "installs conductor packages" do + expect(@chef_run).to upgrade_package "nova-conductor" + end + + it "starts nova-conductor on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-conductor" + end + + it "starts nova-conductor" do + expect(@chef_run).to start_service "nova-conductor" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/default_spec.rb b/chef/cookbooks/openstack-compute/spec/default_spec.rb new file mode 100644 index 0000000..dbbacb8 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/default_spec.rb @@ -0,0 +1,4 @@ +require_relative "spec_helper" + +describe "openstack-compute::default" do +end diff --git a/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb new file mode 100644 index 0000000..4666373 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/identity_registration_spec.rb @@ -0,0 +1,124 @@ +require_relative "spec_helper" + +describe "openstack-compute::identity_registration" do + before do + compute_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::identity_registration" + end + + it "registers service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :tenant_description => "Service Tenant", + :action => [:create_tenant] + ) + end + + it "registers service user" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Service User" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "nova", + :user_pass => "nova-pass", + :action => [:create_user] + ) + end + + it "grants admin role to service user for service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Grant 'admin' Role to Service User for Service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "nova", + :role_name => "admin", + :action => [:grant_role] + ) + end + + it "registers compute service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Compute Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_name => "nova", + :service_type => "compute", + :service_description => "Nova Compute Service", + :action => [:create_service] + ) + end + + it "registers compute endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Compute Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "compute", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", + :endpoint_internalurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", + :endpoint_publicurl => "http://127.0.0.1:8774/v2/%(tenant_id)s", + :action => [:create_endpoint] + ) + end + + it "registers ec2 service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register EC2 Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_name => "ec2", + :service_type => "ec2", + :service_description => "EC2 Compatibility Layer", + :action => [:create_service] + ) + end + + it "registers ec2 endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register EC2 Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "ec2", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "http://127.0.0.1:8773/services/Admin", + :endpoint_internalurl => "http://127.0.0.1:8773/services/Cloud", + :endpoint_publicurl => "http://127.0.0.1:8773/services/Cloud", + :action => [:create_endpoint] + ) + end +end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb new file mode 100644 index 0000000..5b66c1f --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/libvirt-opensuse_spec.rb @@ -0,0 +1,96 @@ +require_relative "spec_helper" + +describe "openstack-compute::libvirt" do + before do + compute_stubs + + # This is stubbed b/c systems without '/boot/grub/menul.lst`, + # fail to pass tests. This can be removed if a check verifies + # the files existence prior to File#open. + ::File.stub(:open).and_call_original + end + + describe "suse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-compute::libvirt" + end + + it "installs libvirt packages" do + expect(@chef_run).to install_package "libvirt" + end + + it "starts libvirt" do + expect(@chef_run).to start_service "libvirtd" + end + + it "starts libvirt on boot" do + expect(@chef_run).to set_service_to_start_on_boot "libvirtd" + end + + describe "libvirtd" do + before do + @file = @chef_run.template "/etc/sysconfig/libvirtd" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies libvirt-bin restart" do + expect(@file).to notify "service[libvirt-bin]", :restart + end + end + + it "installs kvm packages" do + expect(@chef_run).to install_package "kvm" + end + + it "installs qemu packages" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "qemu" + end + chef_run.converge "openstack-compute::libvirt" + expect(chef_run).to install_package "kvm" + end + + it "installs xen packages" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "xen" + end + chef_run.converge "openstack-compute::libvirt" + ["kernel-xen", "xen", "xen-tools"].each do |pkg| + expect(chef_run).to install_package pkg + end + end + + describe "lxc" do + before do + @chef_run_lxc = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |node| + node.set["openstack"]["compute"]["libvirt"]["virt_type"] = "lxc" + end + @chef_run_lxc.converge "openstack-compute::libvirt" + end + + it "installs packages" do + expect(@chef_run_lxc).to install_package "lxc" + end + + it "starts boot.cgroupslxc" do + expect(@chef_run_lxc).to start_service "boot.cgroup" + end + + it "starts boot.cgroups on boot" do + expect(@chef_run_lxc).to set_service_to_start_on_boot "boot.cgroup" + end + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb new file mode 100644 index 0000000..47fa8e7 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/libvirt-redhat_spec.rb @@ -0,0 +1,60 @@ +require_relative "spec_helper" + +describe "openstack-compute::libvirt" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::libvirt" + end + + it "installs libvirt packages" do + expect(@chef_run).to install_package "libvirt" + end + + it "creates libvirtd group and adds nova as a member" do + expect(@chef_run).to create_group "libvirtd" + libvirt_group = @chef_run.group("libvirtd") + libvirt_group.members.should == ["nova"] + end + + it "symlinks qemu-kvm" do + link = @chef_run.link "/usr/bin/qemu-system-x86_64" + expect(link).to link_to "/usr/libexec/qemu-kvm" + end + + it "starts libvirt" do + expect(@chef_run).to start_service "libvirtd" + end + + it "starts libvirt on boot" do + expect(@chef_run).to set_service_to_start_on_boot "libvirtd" + end + + it "does not create /etc/default/libvirt-bin" do + pending "TODO: how to test this" + end + + describe "libvirtd" do + before do + @file = @chef_run.template "/etc/sysconfig/libvirtd" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies libvirt-bin restart" do + expect(@file).to notify "service[libvirt-bin]", :restart + end + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb b/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb new file mode 100644 index 0000000..90d66a2 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/libvirt_spec.rb @@ -0,0 +1,97 @@ +require_relative "spec_helper" + +describe "openstack-compute::libvirt" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::libvirt" + end + + it "installs libvirt packages" do + expect(@chef_run).to install_package "libvirt-bin" + end + + it "does not create libvirtd group and add to nova" do + pending "TODO: how to test this" + end + + it "does not symlink qemu-kvm" do + pending "TODO: how to test this" + end + + it "starts dbus" do + expect(@chef_run).to start_service "dbus" + end + + it "starts dbus on boot" do + expect(@chef_run).to set_service_to_start_on_boot "dbus" + end + + it "starts libvirt" do + expect(@chef_run).to start_service "libvirt-bin" + end + + it "starts libvirt on boot" do + expect(@chef_run).to set_service_to_start_on_boot "libvirt-bin" + end + + it "disables default libvirt network" do + cmd = "virsh net-autostart default --disable" + expect(@chef_run).to execute_command cmd + end + + it "deletes default libvirt network" do + cmd = "virsh net-destroy default" + expect(@chef_run).to execute_command cmd + end + + describe "libvirtd.conf" do + before do + @file = @chef_run.template "/etc/libvirt/libvirtd.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies libvirt-bin restart" do + expect(@file).to notify "service[libvirt-bin]", :restart + end + end + + describe "libvirt-bin" do + before do + @file = @chef_run.template "/etc/default/libvirt-bin" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies libvirt-bin restart" do + expect(@file).to notify "service[libvirt-bin]", :restart + end + end + + it "does not create /etc/sysconfig/libvirtd" do + pending "TODO: how to test this" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb new file mode 100644 index 0000000..599aae8 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/network-redhat_spec.rb @@ -0,0 +1,21 @@ +require_relative "spec_helper" + +describe "openstack-compute::network" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::network" + end + + it "installs nova network packages" do + expect(@chef_run).to upgrade_package "iptables" + expect(@chef_run).to upgrade_package "openstack-nova-network" + end + + it "starts nova network on boot" do + expected = "openstack-nova-network" + expect(@chef_run).to set_service_to_start_on_boot expected + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/network_spec.rb b/chef/cookbooks/openstack-compute/spec/network_spec.rb new file mode 100644 index 0000000..20066f5 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/network_spec.rb @@ -0,0 +1,34 @@ +require_relative "spec_helper" + +describe "openstack-compute::network" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set["openstack"]["compute"]["network"]["service_type"] = "nova" + @chef_run.converge "openstack-compute::network" + end + + expect_runs_nova_common_recipe + + it "installs nova network packages" do + expect(@chef_run).to upgrade_package "iptables" + expect(@chef_run).to upgrade_package "nova-network" + end + + it "starts nova network on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-network" + end + + it "includes openstack-network recipes for quantum when service type is quantum" do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set["openstack"]["compute"]["network"]["service_type"] = "quantum" + @chef_run.converge "openstack-compute::network" + expect(@chef_run).to include_recipe "openstack-network::openvswitch" + expect(@chef_run).to include_recipe "openstack-network::dhcp_agent" + end + + end +end diff --git a/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb new file mode 100644 index 0000000..becd699 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/nova-cert-redhat_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-compute::nova-cert" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::nova-cert" + end + + it "installs nova cert packages" do + expect(@chef_run).to upgrade_package "openstack-nova-cert" + end + + it "starts nova cert on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-nova-cert" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb new file mode 100644 index 0000000..8dc8458 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/nova-cert_spec.rb @@ -0,0 +1,21 @@ +require_relative "spec_helper" + +describe "openstack-compute::nova-cert" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::nova-cert" + end + + expect_runs_nova_common_recipe + + it "installs nova cert packages" do + expect(@chef_run).to upgrade_package "nova-cert" + end + + it "starts nova cert on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-cert" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb new file mode 100644 index 0000000..1cb0ad7 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/nova-common-redhat_spec.rb @@ -0,0 +1,42 @@ +require_relative "spec_helper" + +describe "openstack-compute::nova-common" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::nova-common" + end + + it "runs epel recipe" do + expect(@chef_run).to include_recipe "yum::epel" + end + + it "installs nova common packages" do + expect(@chef_run).to upgrade_package "openstack-nova-common" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-memcached" + end + + describe "nova.conf" do + before do + @file = @chef_run.template "/etc/nova/nova.conf" + # README(shep) need this to evaluate nova.conf.erb template + @chef_run.node.set['cpu'] = Hash.new() + @chef_run.node.set.cpu.total = "2" + end + + it "has correct force_dhcp_release value" do + expect(@chef_run).to create_file_with_content "/etc/nova/nova.conf", + "force_dhcp_release=false" + end + + it "has ec2_private_dns_show_ip enabled" do + expect(@chef_run).to create_file_with_content "/etc/nova/nova.conf", + "ec2_private_dns_show_ip=True" + end + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb new file mode 100644 index 0000000..8b11208 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/nova-common_spec.rb @@ -0,0 +1,341 @@ +require_relative "spec_helper" + +describe "openstack-compute::nova-common" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["mq"] = { + "host" => "127.0.0.1" + } + n.set["openstack"]["compute"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-compute::nova-common" + end + + it "doesn't run epel recipe" do + expect(@chef_run).to_not include_recipe 'yum::epel' + end + + it "runs logging recipe if node attributes say to" do + expect(@chef_run).to include_recipe "openstack-common::logging" + end + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-compute::nova-common" + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + it "can converge with quantum service type" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["network"]["service_type"] = "quantum" + chef_run.converge "openstack-compute::nova-common" + end + + it "installs nova common packages" do + expect(@chef_run).to upgrade_package "nova-common" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-memcache" + end + + describe "/etc/nova" do + before do + @dir = @chef_run.directory "/etc/nova" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "nova", "nova" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + describe "/etc/nova/rootwrap.d" do + before do + @dir = @chef_run.directory "/etc/nova/rootwrap.d" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + describe "nova.conf" do + before do + @file = @chef_run.template "/etc/nova/nova.conf" + # README(shep) need this to evaluate nova.conf.erb template + @chef_run.node.set['cpu'] = Hash.new() + @chef_run.node.set.cpu.total = "2" + end + + it "has proper owner" do + expect(@file).to be_owned_by "nova", "nova" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has rabbit_user" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_userid=guest" + end + + it "has rabbit_password" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_password=rabbit-pass" + end + + it "has rabbit_virtual_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_virtual_host=/" + end + + it "has rabbit_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_hosts" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_hosts=" + end + + it "does not have rabbit_ha_queues" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_ha_queues=" + end + + it "has rabbit_port" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_port=5672" + end + + describe "rabbit ha" do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["compute"]["rabbit"]["ha"] = true + n.set["cpu"] = { + "total" => "2" + } + end + @chef_run.converge "openstack-compute::nova-common" + end + + it "has rabbit_hosts" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" + end + + it "has rabbit_ha_queues" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_ha_queues=True" + end + + it "does not have rabbit_host" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_port" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_port=5672" + end + end + + it "has vncserver_listen" do + expect(@chef_run).to create_file_with_content @file.name, + "vncserver_listen=127.0.1.1" + end + + it "has vncserver_proxyclient_address" do + expect(@chef_run).to create_file_with_content @file.name, + "vncserver_proxyclient_address=127.0.1.1" + end + + it "has xvpvncproxy_host" do + expect(@chef_run).to create_file_with_content @file.name, + "xvpvncproxy_host=127.0.1.1" + end + + it "has novncproxy_host" do + expect(@chef_run).to create_file_with_content @file.name, + "novncproxy_host=127.0.1.1" + end + + it "has correct force_dhcp_release value" do + expect(@chef_run).to create_file_with_content @file.name, + "force_dhcp_release=true" + end + + it "has virtio enabled" do + expect(@chef_run).to create_file_with_content @file.name, + "libvirt_use_virtio_for_bridges=true" + end + + it "does not have ec2_private_dns_show_ip option" do + expect(@chef_run).to_not create_file_with_content @file.name, + "ec2_private_dns_show_ip" + end + end + + +# describe "identity role local node" do +# before do +# @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| +# n.set["openstack"]["identity"]["admin_tenant_name"] = "admin-tenant" +# n.set["openstack"]["identity"]["admin_user"] = "admin-user" +# end +# @chef_run.converge 'role[os-identity]', "openstack-compute::nova-common" +# end +# it "has keystone_hash" do +# expect(@chef_run).to log 'openstack-compute::nova-common:keystone|node[???]' +# end +# it "has ksadmin_user" do +# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_user|admin-user' +# end +# it "has ksadmin_tenant_name" do +# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_tenant_name|admin-tenant' +# end +# end + + +# describe "identity role search" do +# before do +# @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| +# n.set["openstack"]["compute"]["identity_service_chef_role"] = "os-identity" +# end +# @chef_run.converge "openstack-compute::nova-common" +# end +# it "has keystone_hash" do +# expect(@chef_run).to log 'openstack-compute::nova-common:keystone|node[???]' +# end +# it "has ksadmin_user" do +# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_user|admin-user' +# end +# it "has ksadmin_tenant_name" do +# expect(@chef_run).to log 'openstack-compute::nova-common:ksadmin_tenant_name|admin-tenant' +# end +# end + + describe "rootwrap.conf" do + before do + @file = @chef_run.template "/etc/nova/rootwrap.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + describe "api-metadata.filters" do + before do + @file = @chef_run.template "/etc/nova/rootwrap.d/api-metadata.filters" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + describe "compute.filters" do + before do + @file = @chef_run.template "/etc/nova/rootwrap.d/compute.filters" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + describe "network.filters" do + before do + @file = @chef_run.template "/etc/nova/rootwrap.d/network.filters" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + describe "openrc" do + before do + @file = @chef_run.template "/root/openrc" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "contains ksadmin_user" do + expect(@chef_run).to create_file_with_content @file.name, + "export OS_USERNAME=admin-user" + end + + it "contains ksadmin_tenant_name" do + expect(@chef_run).to create_file_with_content @file.name, + "export OS_TENANT_NAME=admin-tenant" + end + + it "contains ksadmin_pass" do + expect(@chef_run).to create_file_with_content @file.name, + "export OS_PASSWORD=admin-pass" + end + + it "rest of template contents" do + pending "TODO: implement" + end + end + + it "enables nova login" do + cmd = "usermod -s /bin/sh nova" + expect(@chef_run).to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb b/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb new file mode 100644 index 0000000..01c88a1 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/nova-setup_spec.rb @@ -0,0 +1,71 @@ +require_relative "spec_helper" + +describe "openstack-compute::nova-setup" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::nova-setup" + end + + expect_runs_nova_common_recipe + + it "runs db migrations" do + cmd = "nova-manage db sync" + expect(@chef_run).to execute_command cmd + end + + it "adds nova network ipv4 addresses" do + cmd = ["nova-manage network create --label=public", + "--fixed_range_v4=192.168.100.0/24", + "--multi_host='T'", + "--num_networks=1", + "--network_size=255", + "--bridge=br100", + "--bridge_interface=eth2", + "--dns1=8.8.8.8", + "--dns2=8.8.4.4"].join(' ') + expect(@chef_run).to execute_command cmd + end + + it "add_floaters.py has proper modes" do + file = @chef_run.cookbook_file "/usr/local/bin/add_floaters.py" + expect(sprintf("%o", file.mode)).to eq "755" + end + + it "adds cidr range of floating ipv4 addresses" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["network"]["floating"]["ipv4_cidr"] = "10.10.10.0/24" + chef_run.converge "openstack-compute::nova-setup" + + cmd = "/usr/local/bin/add_floaters.py nova --cidr=10.10.10.0/24" + expect(chef_run).to execute_command cmd + end + + it "adds range of floating ipv4 addresses" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["network"] = { + "floating" => { + "ipv4_range" => "10.10.10.1,10.10.10.5" + } + } + chef_run.converge "openstack-compute::nova-setup" + + cmd = "/usr/local/bin/add_floaters.py nova --ip-range=10.10.10.1,10.10.10.5" + expect(chef_run).to execute_command cmd + end + + it "adds cidr range of floating ipv4 addresses to neutron" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["compute"]["network"]["service_type"] = "neutron" + node.set["openstack"]["compute"]["network"]["floating"]["ipv4_cidr"] = "10.10.10.0/24" + node.set["openstack"]["compute"]["network"]["floating"]["public_network_name"] = "public" + chef_run.converge "openstack-compute::nova-setup" + cmd = ". /root/openrc && /usr/local/bin/add_floaters.py neutron --cidr=10.10.10.0/24 --pool=public" + expect(chef_run).to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb new file mode 100644 index 0000000..a66ec01 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/scheduler-redhat_spec.rb @@ -0,0 +1,24 @@ +require_relative "spec_helper" + +describe "openstack-compute::scheduler" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::scheduler" + end + + it "installs nova scheduler packages" do + expect(@chef_run).to upgrade_package "openstack-nova-scheduler" + end + + it "starts nova scheduler" do + expect(@chef_run).to start_service "openstack-nova-scheduler" + end + + it "starts nova scheduler on boot" do + expected = "openstack-nova-scheduler" + expect(@chef_run).to set_service_to_start_on_boot expected + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb b/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb new file mode 100644 index 0000000..bd1b79c --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/scheduler_spec.rb @@ -0,0 +1,27 @@ +require_relative "spec_helper" + +describe "openstack-compute::scheduler" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::scheduler" + end + + expect_runs_nova_common_recipe + + expect_creates_nova_lock_dir + + it "installs nova scheduler packages" do + expect(@chef_run).to upgrade_package "nova-scheduler" + end + + it "starts nova scheduler" do + expect(@chef_run).to start_service "nova-scheduler" + end + + it "starts nova scheduler on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-scheduler" + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/spec_helper.rb b/chef/cookbooks/openstack-compute/spec/spec_helper.rb new file mode 100644 index 0000000..e75a2aa --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/spec_helper.rb @@ -0,0 +1,111 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def compute_stubs + ::Chef::Recipe.any_instance.stub(:rabbit_servers). + and_return "1.1.1.1:5672,2.2.2.2:5672" + ::Chef::Recipe.any_instance.stub(:address_for). + with("lo"). + and_return "127.0.1.1" + ::Chef::Recipe.any_instance.stub(:search_for). + with("os-identity").and_return( + [{ + 'openstack' => { + 'identity' => { + 'admin_tenant_name' => 'admin-tenant', + 'admin_user' => 'admin-user' + } + } + }] + ) + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "openstack_identity_bootstrap_token"). + and_return "bootstrap-token" + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "quantum_metadata_secret"). + and_return "metadata-secret" + ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password). + with("guest"). + and_return "rabbit-pass" + ::Chef::Recipe.any_instance.stub(:user_password). + with("admin-user"). + and_return "admin-pass" + ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-compute"). + and_return "nova-pass" + ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-network"). + and_return "quantum-pass" + ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] + ::Chef::Recipe.any_instance.stub(:system). + with("grub2-set-default 'openSUSE GNU/Linux, with Xen hypervisor'"). + and_return true +end + +def expect_runs_nova_common_recipe + it "installs nova-common" do + expect(@chef_run).to include_recipe "openstack-compute::nova-common" + end +end + +def expect_installs_python_keystone + it "installs python-keystone" do + expect(@chef_run).to upgrade_package "python-keystone" + end +end + +def expect_creates_nova_lock_dir + describe "/var/lock/nova" do + before do + @dir = @chef_run.directory "/var/lock/nova" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "nova", "nova" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end +end + +def expect_creates_api_paste service, action=:restart + describe "api-paste.ini" do + before do + @file = @chef_run.template "/etc/nova/api-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "nova", "nova" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies nova-api-ec2 restart" do + expect(@file).to notify service, action + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb b/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb new file mode 100644 index 0000000..dcffe42 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/vncproxy-redhat_spec.rb @@ -0,0 +1,25 @@ +require_relative "spec_helper" + +describe "openstack-compute::vncproxy" do + before { compute_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-compute::vncproxy" + end + + it "starts nova vncproxy on boot" do + expected = "openstack-nova-novncproxy" + expect(@chef_run).to set_service_to_start_on_boot expected + end + + it "starts nova consoleauth" do + expect(@chef_run).to start_service "openstack-nova-console" + end + + it "starts nova consoleauth on boot" do + expected = "openstack-nova-console" + expect(@chef_run).to set_service_to_start_on_boot expected + end + end +end diff --git a/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb b/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb new file mode 100644 index 0000000..c800b46 --- /dev/null +++ b/chef/cookbooks/openstack-compute/spec/vncproxy_spec.rb @@ -0,0 +1,35 @@ +require_relative "spec_helper" + +describe "openstack-compute::vncproxy" do + before { compute_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-compute::vncproxy" + end + + expect_runs_nova_common_recipe + + it "installs vncproxy packages" do + expect(@chef_run).to upgrade_package "novnc" + expect(@chef_run).to upgrade_package "websockify" + expect(@chef_run).to upgrade_package "nova-novncproxy" + end + + it "installs consoleauth packages" do + expect(@chef_run).to upgrade_package "nova-consoleauth" + end + + it "starts nova vncproxy on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-novncproxy" + end + + it "starts nova consoleauth" do + expect(@chef_run).to start_service "nova-consoleauth" + end + + it "starts nova consoleauth on boot" do + expect(@chef_run).to set_service_to_start_on_boot "nova-consoleauth" + end + end +end diff --git a/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb new file mode 100644 index 0000000..7056248 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/api-paste.ini.erb @@ -0,0 +1,113 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +############ +# Metadata # +############ +[composite:metadata] +use = egg:Paste#urlmap +/: meta + +[pipeline:meta] +pipeline = ec2faultwrap logrequest metaapp + +[app:metaapp] +paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory + +####### +# EC2 # +####### + +[composite:ec2] +use = egg:Paste#urlmap +/services/Cloud: ec2cloud + +[composite:ec2cloud] +use = call:nova.api.auth:pipeline_factory +noauth = ec2faultwrap logrequest ec2noauth cloudrequest validator ec2executor +keystone = ec2faultwrap logrequest ec2keystoneauth cloudrequest validator ec2executor + +[filter:ec2faultwrap] +paste.filter_factory = nova.api.ec2:FaultWrapper.factory + +[filter:logrequest] +paste.filter_factory = nova.api.ec2:RequestLogging.factory + +[filter:ec2lockout] +paste.filter_factory = nova.api.ec2:Lockout.factory + +[filter:ec2keystoneauth] +paste.filter_factory = nova.api.ec2:EC2KeystoneAuth.factory + +[filter:ec2noauth] +paste.filter_factory = nova.api.ec2:NoAuth.factory + +[filter:cloudrequest] +controller = nova.api.ec2.cloud.CloudController +paste.filter_factory = nova.api.ec2:Requestify.factory + +[filter:authorizer] +paste.filter_factory = nova.api.ec2:Authorizer.factory + +[filter:validator] +paste.filter_factory = nova.api.ec2:Validator.factory + +[app:ec2executor] +paste.app_factory = nova.api.ec2:Executor.factory + +############# +# Openstack # +############# + +[composite:osapi_compute] +use = call:nova.api.openstack.urlmap:urlmap_factory +/: oscomputeversions +/v1.1: openstack_compute_api_v2 +/v2: openstack_compute_api_v2 + +[composite:openstack_compute_api_v2] +use = call:nova.api.auth:pipeline_factory +noauth = faultwrap sizelimit noauth ratelimit osapi_compute_app_v2 +keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2 +keystone_nolimit = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2 + +[filter:faultwrap] +paste.filter_factory = nova.api.openstack:FaultWrapper.factory + +[filter:noauth] +paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory + +[filter:ratelimit] +paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory +limits = +<%= node["openstack"]["compute"]["ratelimit"]["settings"].values.inject([]) { |output,v| output << " ( #{v['verb']}, #{v['uri']}, #{v['regex']}, #{v['limit']}, #{v['interval']} )" }.join(";\n") %> + +[filter:sizelimit] +paste.filter_factory = nova.api.sizelimit:RequestBodySizeLimiter.factory + +[app:osapi_compute_app_v2] +paste.app_factory = nova.api.openstack.compute:APIRouter.factory + +[pipeline:oscomputeversions] +pipeline = faultwrap oscomputeversionapp + +[app:oscomputeversionapp] +paste.app_factory = nova.api.openstack.compute.versions:Versions.factory + +########## +# Shared # +########## + +[filter:keystonecontext] +paste.filter_factory = nova.api.auth:NovaKeystoneContext.factory + +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_uri = <%= @auth_uri %> +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +auth_version = <%= node["openstack"]["compute"]["api"]["auth"]["version"] %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["compute"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-compute/templates/default/collectd-plugin-mysql.conf.erb b/chef/cookbooks/openstack-compute/templates/default/collectd-plugin-mysql.conf.erb new file mode 100644 index 0000000..ec87b07 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/collectd-plugin-mysql.conf.erb @@ -0,0 +1,18 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +LoadPlugin "<%= @name %>" +<% if not @options.empty? %> +"> + <% @options[:databases].each_pair do |db, vars| %> + "> + <% vars.each_pair do |key, value| + if value.is_a? Array + value.each do |subvalue| %> + <%= collectd_key(key) %> <%= collectd_option(subvalue) %> + <% end else %> + <%= collectd_key(key) %> <%= collectd_option(value) %> + <% end end %> + + <% end %> + +<% end %> diff --git a/chef/cookbooks/openstack-compute/templates/default/libvirt-bin.erb b/chef/cookbooks/openstack-compute/templates/default/libvirt-bin.erb new file mode 100644 index 0000000..e9a6848 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/libvirt-bin.erb @@ -0,0 +1,13 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# Defaults for libvirt-bin initscript (/etc/init.d/libvirt-bin) +# This is a POSIX shell fragment + +# Start libvirtd to handle qemu/kvm: +start_libvirtd="yes" + +# options passed to libvirtd, add "-l" to listen on tcp +libvirtd_opts="-d -l" + +# pass in location of kerberos keytab +#export KRB5_KTNAME=/etc/libvirt/libvirt.keytab diff --git a/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb b/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb new file mode 100644 index 0000000..ca82e4a --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/libvirtd.conf.erb @@ -0,0 +1,395 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# Master libvirt daemon configuration file +# +# For further information consult http://libvirt.org/format.html +# +# NOTE: the tests/daemon-conf regression test script requires +# that each "PARAMETER = VALUE" line in this file have the parameter +# name just after a leading "#". + +################################################################# +# +# Network connectivity controls +# + +# Flag listening for secure TLS connections on the public TCP/IP port. +# NB, must pass the --listen flag to the libvirtd process for this to +# have any effect. +# +# It is necessary to setup a CA and issue server certificates before +# using this capability. +# +# This is enabled by default, uncomment this to disable it +listen_tls = 0 + +# Listen for unencrypted TCP connections on the public TCP/IP port. +# NB, must pass the --listen flag to the libvirtd process for this to +# have any effect. +# +# Using the TCP socket requires SASL authentication by default. Only +# SASL mechanisms which support data encryption are allowed. This is +# DIGEST_MD5 and GSSAPI (Kerberos5) +# +# This is disabled by default, uncomment this to enable it. +listen_tcp = 1 + + + +# Override the port for accepting secure TLS connections +# This can be a port number, or service name +# +#tls_port = "16514" + +# Override the port for accepting insecure TCP connections +# This can be a port number, or service name +# +#tcp_port = "16509" + + +# Override the default configuration which binds to all network +# interfaces. This can be a numeric IPv4/6 address, or hostname +# +#listen_addr = "192.168.0.1" + + +# Flag toggling mDNS advertizement of the libvirt service. +# +# Alternatively can disable for all services on a host by +# stopping the Avahi daemon +# +# This is enabled by default, uncomment this to disable it +#mdns_adv = 0 + +# Override the default mDNS advertizement name. This must be +# unique on the immediate broadcast network. +# +# The default is "Virtualization Host HOSTNAME", where HOSTNAME +# is subsituted for the short hostname of the machine (without domain) +# +#mdns_name = "Virtualization Host Joe Demo" + + +################################################################# +# +# UNIX socket access controls +# + +# Set the UNIX domain socket group ownership. This can be used to +# allow a 'trusted' set of users access to management capabilities +# without becoming root. +# +# This is restricted to 'root' by default. +unix_sock_group = "<%= @libvirt_group %>" + +# Set the UNIX socket permissions for the R/O socket. This is used +# for monitoring VM status only +# +# Default allows any user. If setting group ownership may want to +# restrict this to: +#unix_sock_ro_perms = "0777" + +# Set the UNIX socket permissions for the R/W socket. This is used +# for full management of VMs +# +# Default allows only root. If PolicyKit is enabled on the socket, +# the default will change to allow everyone (eg, 0777) +# +# If not using PolicyKit and setting group ownership for access +# control then you may want to relax this to: +unix_sock_rw_perms = "0770" + +# Set the name of the directory in which sockets will be found/created. +#unix_sock_dir = "/var/run/libvirt" + +################################################################# +# +# Authentication. +# +# - none: do not perform auth checks. If you can connect to the +# socket you are allowed. This is suitable if there are +# restrictions on connecting to the socket (eg, UNIX +# socket permissions), or if there is a lower layer in +# the network providing auth (eg, TLS/x509 certificates) +# +# - sasl: use SASL infrastructure. The actual auth scheme is then +# controlled from /etc/sasl2/libvirt.conf. For the TCP +# socket only GSSAPI & DIGEST-MD5 mechanisms will be used. +# For non-TCP or TLS sockets, any scheme is allowed. +# +# - polkit: use PolicyKit to authenticate. This is only suitable +# for use on the UNIX sockets. The default policy will +# require a user to supply their own password to gain +# full read/write access (aka sudo like), while anyone +# is allowed read/only access. +# +# Set an authentication scheme for UNIX read-only sockets +# By default socket permissions allow anyone to connect +# +# To restrict monitoring of domains you may wish to enable +# an authentication mechanism here +auth_unix_ro = "none" + +# Set an authentication scheme for UNIX read-write sockets +# By default socket permissions only allow root. If PolicyKit +# support was compiled into libvirt, the default will be to +# use 'polkit' auth. +# +# If the unix_sock_rw_perms are changed you may wish to enable +# an authentication mechanism here +auth_unix_rw = "none" + +# Change the authentication scheme for TCP sockets. +# +# If you don't enable SASL, then all TCP traffic is cleartext. +# Don't do this outside of a dev/test scenario. For real world +# use, always enable SASL and use the GSSAPI or DIGEST-MD5 +# mechanism in /etc/sasl2/libvirt.conf +auth_tcp = "<%= @auth_tcp %>" + +# Change the authentication scheme for TLS sockets. +# +# TLS sockets already have encryption provided by the TLS +# layer, and limited authentication is done by certificates +# +# It is possible to make use of any SASL authentication +# mechanism as well, by using 'sasl' for this option +#auth_tls = "none" + + + +################################################################# +# +# TLS x509 certificate configuration +# + + +# Override the default server key file path +# +#key_file = "/etc/pki/libvirt/private/serverkey.pem" + +# Override the default server certificate file path +# +#cert_file = "/etc/pki/libvirt/servercert.pem" + +# Override the default CA certificate path +# +#ca_file = "/etc/pki/CA/cacert.pem" + +# Specify a certificate revocation list. +# +# Defaults to not using a CRL, uncomment to enable it +#crl_file = "/etc/pki/CA/crl.pem" + + + +################################################################# +# +# Authorization controls +# + + +# Flag to disable verification of our own server certificates +# +# When libvirtd starts it performs some sanity checks against +# its own certificates. +# +# Default is to always run sanity checks. Uncommenting this +# will disable sanity checks which is not a good idea +#tls_no_sanity_certificate = 1 + +# Flag to disable verification of client certificates +# +# Client certificate verification is the primary authentication mechanism. +# Any client which does not present a certificate signed by the CA +# will be rejected. +# +# Default is to always verify. Uncommenting this will disable +# verification - make sure an IP whitelist is set +#tls_no_verify_certificate = 1 + + +# A whitelist of allowed x509 Distinguished Names +# This list may contain wildcards such as +# +# "C=GB,ST=London,L=London,O=Red Hat,CN=*" +# +# See the POSIX fnmatch function for the format of the wildcards. +# +# NB If this is an empty list, no client can connect, so comment out +# entirely rather than using empty list to disable these checks +# +# By default, no DN's are checked +#tls_allowed_dn_list = ["DN1", "DN2"] + + +# A whitelist of allowed SASL usernames. The format for usernames +# depends on the SASL authentication mechanism. Kerberos usernames +# look like username@REALM +# +# This list may contain wildcards such as +# +# "*@EXAMPLE.COM" +# +# See the POSIX fnmatch function for the format of the wildcards. +# +# NB If this is an empty list, no client can connect, so comment out +# entirely rather than using empty list to disable these checks +# +# By default, no Username's are checked +#sasl_allowed_username_list = ["joe@EXAMPLE.COM", "fred@EXAMPLE.COM" ] + + + +################################################################# +# +# Processing controls +# + +# The maximum number of concurrent client connections to allow +# over all sockets combined. +#max_clients = 20 + + +# The minimum limit sets the number of workers to start up +# initially. If the number of active clients exceeds this, +# then more threads are spawned, upto max_workers limit. +# Typically you'd want max_workers to equal maximum number +# of clients allowed +#min_workers = 5 +#max_workers = 20 + + +# The number of priority workers. If all workers from above +# pool will stuck, some calls marked as high priority +# (notably domainDestroy) can be executed in this pool. +#prio_workers = 5 + +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +# +# XXX this isn't actually enforced yet, only the per-client +# limit is used so far +#max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +#max_client_requests = 5 + +################################################################# +# +# Logging controls +# + +# Logging level: 4 errors, 3 warnings, 2 information, 1 debug +# basically 1 will log everything possible +#log_level = 3 + +# Logging filters: +# A filter allows to select a different logging level for a given category +# of logs +# The format for a filter is: +# x:name +# where name is a match string e.g. remote or qemu +# the x prefix is the minimal level where matching messages should be logged +# 1: DEBUG +# 2: INFO +# 3: WARNING +# 4: ERROR +# +# Multiple filter can be defined in a single @filters, they just need to be +# separated by spaces. +# +# e.g: +# log_filters="3:remote 4:event" +# to only get warning or errors from the remote layer and only errors from +# the event layer. + +# Logging outputs: +# An output is one of the places to save logging information +# The format for an output can be: +# x:stderr +# output goes to stderr +# x:syslog:name +# use syslog for the output and use the given name as the ident +# x:file:file_path +# output to a file, with the given filepath +# In all case the x prefix is the minimal level, acting as a filter +# 1: DEBUG +# 2: INFO +# 3: WARNING +# 4: ERROR +# +# Multiple output can be defined, they just need to be separated by spaces. +# e.g.: +# log_outputs="3:syslog:libvirtd" +# to log all warnings and errors to syslog under the libvirtd ident + +# Log debug buffer size: default 64 +# The daemon keeps an internal debug log buffer which will be dumped in case +# of crash or upon receiving a SIGUSR2 signal. This setting allows to override +# the default buffer size in kilobytes. +# If value is 0 or less the debug log buffer is deactivated +#log_buffer_size = 64 + + +################################################################## +# +# Auditing +# +# This setting allows usage of the auditing subsystem to be altered: +# +# audit_level == 0 -> disable all auditing +# audit_level == 1 -> enable auditing, only if enabled on host (default) +# audit_level == 2 -> enable auditing, and exit if disabled on host +# +#audit_level = 2 +# +# If set to 1, then audit messages will also be sent +# via libvirt logging infrastructure. Defaults to 0 +# +#audit_logging = 1 + +################################################################### +# UUID of the host: +# Provide the UUID of the host here in case the command +# 'dmidecode -s system-uuid' does not provide a valid uuid. In case +# 'dmidecode' does not provide a valid UUID and none is provided here, a +# temporary UUID will be generated. +# Keep the format of the example UUID below. UUID must not have all digits +# be the same. + +# NB This default all-zeros UUID will not work. Replace +# it with the output of the 'uuidgen' command and then +# uncomment this entry +#host_uuid = "00000000-0000-0000-0000-000000000000" + +################################################################### +# Keepalive protocol: +# This allows libvirtd to detect broken client connections or even +# dead client. A keepalive message is sent to a client after +# keepalive_interval seconds of inactivity to check if the client is +# still responding; keepalive_count is a maximum number of keepalive +# messages that are allowed to be sent to the client without getting +# any response before the connection is considered broken. In other +# words, the connection is automatically closed approximately after +# keepalive_interval * (keepalive_count + 1) seconds since the last +# message received from the client. If keepalive_interval is set to +# -1, libvirtd will never send keepalive requests; however clients +# can still send them and the deamon will send responses. When +# keepalive_count is set to 0, connections will be automatically +# closed after keepalive_interval seconds of inactivity without +# sending any keepalive messages. +# +#keepalive_interval = 5 +#keepalive_count = 5 +# +# If set to 1, libvirtd will refuse to talk to clients that do not +# support keepalive protocol. Defaults to 0. +# +#keepalive_required = 1 diff --git a/chef/cookbooks/openstack-compute/templates/default/libvirtd.erb b/chef/cookbooks/openstack-compute/templates/default/libvirtd.erb new file mode 100644 index 0000000..caa2351 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/libvirtd.erb @@ -0,0 +1,26 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# Override the default config file +# NOTE: This setting is no longer honoured if using +# systemd. Set '--config /etc/libvirt/libvirtd.conf' +# in LIBVIRTD_ARGS instead. +#LIBVIRTD_CONFIG=/etc/libvirt/libvirtd.conf + +# Listen for TCP/IP connections +# NB. must setup TLS/SSL keys prior to using this +LIBVIRTD_ARGS="--listen" + +# Override Kerberos service keytab for SASL/GSSAPI +#KRB5_KTNAME=/etc/libvirt/krb5.tab + +# Override the QEMU/SDL default audio driver probing when +# starting virtual machines using SDL graphics +# +# NB these have no effect for VMs using VNC, unless vnc_allow_host_audio +# is enabled in /etc/libvirt/qemu.conf +#QEMU_AUDIO_DRV=sdl +# +#SDL_AUDIODRIVER=pulse + +# Override the maximum number of opened files +#LIBVIRTD_NOFILES_LIMIT=2048 diff --git a/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb b/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb new file mode 100644 index 0000000..eabd1fc --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/nova.conf.erb @@ -0,0 +1,225 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +[DEFAULT] +# LOGS/STATE +debug=<%= node["openstack"]["compute"]["debug"] %> +verbose=<%= node["openstack"]["compute"]["verbose"] %> +auth_strategy=<%= node["openstack"]["compute"]["api"]["auth_strategy"] %> +dhcpbridge_flagfile=/etc/nova/nova.conf +dhcpbridge=/usr/bin/nova-dhcpbridge +logdir=/var/log/nova +<% if node["openstack"]["compute"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> +state_path=/var/lib/nova +lock_path=/var/lock/nova + +##### RABBITMQ ##### +rabbit_userid=<%= node['openstack']['mq']['user'] %> +rabbit_password=<%= node['openstack']['mq']['password'] %> +rabbit_virtual_host=<%= node["openstack"]["compute"]["rabbit"]["vhost"] %> +<% if node["openstack"]["compute"]["rabbit"]["ha"] -%> +rabbit_hosts=<%= @rabbit_hosts %> +rabbit_ha_queues=True +<% else -%> +rabbit_host=<%= node['openstack']['mq']['bind_address'] %> +rabbit_port=<%= node['openstack']['mq']['port'] %> +<% end -%> + +##### SCHEDULER ##### +# scheduler_manager=nova.scheduler.manager.SchedulerManager +compute_scheduler_driver=<%= node["openstack"]["compute"]["scheduler"]["scheduler_driver"] %> +scheduler_available_filters=nova.scheduler.filters.all_filters +# which filter class names to use for filtering hosts when not specified in the request. +scheduler_default_filters=<%= @scheduler_default_filters %> +default_availability_zone=<%= node["openstack"]["compute"]["config"]["availability_zone"] %> +default_schedule_zone=<%= node["openstack"]["compute"]["config"]["default_schedule_zone"] %> +storage_availability_zone=<%= node["openstack"]["compute"]["config"]["storage_availability_zone"] %> + +##### NETWORK ##### +<% case node["openstack"]["compute"]["network"]["service_type"] + +when "quantum" -%> +# N.B. due to https://bugs.launchpad.net/nova/+bug/1206330 +# we override the endpoint scheme below, ignore the port +# and essentially force http +<% if @network_endpoint.port == 443 -%> +quantum_url=http://<%= @network_endpoint.host %>:80 +<% else -%> +quantum_url=http://<%= @network_endpoint.host %>:<%= @network_endpoint.port %> +<% end -%> +network_api_class=<%= node["openstack"]["compute"]["network"]["quantum"]["network_api_class"] %> +quantum_auth_strategy=<%= node["openstack"]["compute"]["network"]["quantum"]["auth_strategy"] %> +quantum_admin_tenant_name=<%= node['openstack']['identity']['network']['tenant'] %> +quantum_admin_username=<%= node['openstack']['identity']['network']['username'] %> +quantum_admin_password=<%= node['openstack']['identity']['network']['password'] %> +quantum_admin_auth_url=<%= @identity_endpoint.to_s %> +libvirt_vif_driver=<%= node["openstack"]["compute"]["network"]["quantum"]["libvirt_vif_driver"] %> +linuxnet_interface_driver=<%= node["openstack"]["compute"]["network"]["quantum"]["linuxnet_interface_driver"] %> +firewall_driver = nova.virt.firewall.NoopFirewallDriver +security_group_api=<%= node["openstack"]["compute"]["network"]["quantum"]["security_group_api"] %> +service_quantum_metadata_proxy=<%= node["openstack"]["compute"]["network"]["quantum"]["service_quantum_metadata_proxy"] %> +quantum_metadata_proxy_shared_secret=<%= @quantum_metadata_proxy_shared_secret %> +default_floating_pool=<%= node["openstack"]["compute"]["network"]["quantum"]["public_network_name"] %> +dns_server=<%= node["openstack"]["compute"]["network"]["quantum"]["dns_server"] %> + +<% when "nova" -%> +multi_host=<%= node["openstack"]["compute"]["network"]["multi_host"] %> +network_manager=<%= node["openstack"]["compute"]["network"]["network_manager"] %> +public_interface=<%= node["openstack"]["compute"]["network"]["public_interface"] %> +fixed_range=<%= node["openstack"]["compute"]["network"]["fixed_range"] %> +dmz_cidr=<%= node["openstack"]["compute"]["network"]["dmz_cidr"] %> +<% if %w(fedora redhat centos).include? node.platform -%> +# https://bugzilla.redhat.com/show_bug.cgi?id=788485 - not released in epel yet +force_dhcp_release=false +<% else -%> +force_dhcp_release=true +<% end -%> +<% if node["openstack"]["compute"]["dhcp_domain"] -%> +dhcp_domain=<%= node["openstack"]["compute"]["dhcp_domain"] %> +<% end %> +send_arp_for_ha=true +use_single_default_gateway=<%= node["openstack"]["compute"]["network"]["use_single_default_gateway"] %> +<% if node["openstack"]["compute"]["libvirt"]["virt_type"] == "qemu" -%> +libvirt_use_virtio_for_bridges=false +<% else -%> +libvirt_use_virtio_for_bridges=true +<% end -%> +vlan_interface=<%= node["openstack"]["compute"]["network"]["vlan_interface"] %> + +<% end -%> + +##### GLANCE ##### +image_service=nova.image.glance.GlanceImageService +glance_api_servers=<%= @glance_api_ipaddress %>:<%= @glance_api_port %> + +##### COMPUTE ##### +compute_driver=<%= node["openstack"]["compute"]["driver"] %> +compute_manager=nova.compute.manager.ComputeManager +sql_connection=<%= @sql_connection %> +connection_type=libvirt +libvirt_type=<%= node["openstack"]["compute"]["libvirt"]["virt_type"] %> +# Command prefix to use for running commands as root (default: sudo) +rootwrap_config=/etc/nova/rootwrap.conf +# Should unused base images be removed? (default: false) +remove_unused_base_images=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_base_images"] %> +# Unused resized base images younger than this will not be removed (default: 3600) +remove_unused_resized_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_resized_minimum_age_seconds"] %> +# Unused unresized base images younger than this will not be removed (default: 86400) +remove_unused_original_minimum_age_seconds=<%= node["openstack"]["compute"]["libvirt"]["remove_unused_original_minimum_age_seconds"] %> +# Write a checksum for files in _base to disk (default: false) +checksum_base_images=<%= node["openstack"]["compute"]["libvirt"]["checksum_base_images"] %> + +##### VNCPROXY ##### +novncproxy_base_url=<%= @novncproxy_base_url %> +#xvpvncproxy_base_url=<%= @xvpvncproxy_base_url %> + +# This is only required on the server running xvpvncproxy +# xvpvncproxy_host=<%= node['openstack']['endpoints']['compute-xvpvnc']['host'] %> +# xvpvncproxy_port=<%= node['openstack']['endpoints']['compute-xvpvnc']['port'] %> + +# This is only required on the server running novncproxy +#novncproxy_host=<%= node['openstack']['endpoints']['compute-novnc']['host'] %> +novncproxy_port=<%= node['openstack']['endpoints']['compute-novnc']['port'] %> + +vncserver_listen=<%= @vncserver_listen %> +vncserver_proxyclient_address=<%= @vncserver_proxyclient_address %> + +# store consoleauth tokens in memcached +<% unless @memcache_servers.empty? -%> +memcached_servers=<%= @memcache_servers %> +<% end -%> + +##### MISC ##### +# force backing images to raw format +force_raw_images=<%= node["openstack"]["compute"]["config"]["force_raw_images"] %> +allow_same_net_traffic=<%= node["openstack"]["compute"]["config"]["allow_same_net_traffic"] %> +osapi_max_limit=<%= node["openstack"]["compute"]["config"]["osapi_max_limit"] %> +# If you terminate SSL with a load balancer, the HTTP_HOST environ +# variable that generates the request_uri in webob.Request will lack +# the HTTPS scheme. Setting this overrides the default and allows +# URIs returned in the various links collections to contain the proper +# HTTPS endpoint. +osapi_compute_link_prefix = <%= @osapi_compute_link_prefix %> +snapshot_image_format=<%= node["openstack"]["compute"]["config"]["snapshot_image_format"] %> +start_guests_on_host_boot=<%= node["openstack"]["compute"]["config"]["start_guests_on_host_boot"] %> +resume_guests_state_on_host_boot=<%= node["openstack"]["compute"]["config"]["resume_guests_state_on_host_boot"] %> + +##### QUOTAS ##### +# (StrOpt) default driver to use for quota checks (default: nova.quota.DbQuotaDriver) +quota_driver=<%= node["openstack"]["compute"]["config"]["quota_driver"] %> +# number of security groups per project (default: 10) +quota_security_groups=<%= node["openstack"]["compute"]["config"]["quota_security_groups"] %> +# number of security rules per security group (default: 20) +quota_security_group_rules=<%= node["openstack"]["compute"]["config"]["quota_security_group_rules"] %> +# number of instance cores allowed per project (default: 20) +quota_cores=<%= node["openstack"]["compute"]["config"]["quota_cores"] %> +# number of fixed ips allowed per project (this should be at least the number of instances allowed) (default: -1) +quota_fixed_ips=<%= node["openstack"]["compute"]["config"]["quota_fixed_ips"] %> +# number of floating ips allowed per project (default: 10) +quota_floating_ips=<%= node["openstack"]["compute"]["config"]["quota_floating_ips"] %> +# number of bytes allowed per injected file (default: 10240) +quota_injected_file_content_bytes=<%= node["openstack"]["compute"]["config"]["quota_injected_file_content_bytes"] %> +# number of bytes allowed per injected file path (default: 255) +quota_injected_file_path_bytes=<%= node["openstack"]["compute"]["config"]["quota_injected_file_path_bytes"] %> +# number of injected files allowed (default: 5) +quota_injected_files=<%= node["openstack"]["compute"]["config"]["quota_injected_files"] %> +# number of instances allowed per project (defailt: 10) +quota_instances=<%= node["openstack"]["compute"]["config"]["quota_instances"] %> +# number of key pairs per user (default: 100) +quota_key_pairs=<%= node["openstack"]["compute"]["config"]["quota_key_pairs"] %> +# number of metadata items allowed per instance (default: 128) +quota_metadata_items=<%= node["openstack"]["compute"]["config"]["quota_metadata_items"] %> +# megabytes of instance ram allowed per project (default: 51200) +quota_ram=<%= node["openstack"]["compute"]["config"]["quota_ram"] %> + + +<%- if /FilterScheduler/.match(node["openstack"]["compute"]["scheduler"]["scheduler_driver"]) or + /MultiScheduler/.match(node["openstack"]["compute"]["scheduler"]["scheduler_driver"]) %> +# FilterScheduler Only Options +<%- if /ComputeFilter/.match(@scheduler_default_filters) %> +# virtual CPU to Physical CPU allocation ratio (default: 16.0) +cpu_allocation_ratio=<%= node["openstack"]["compute"]["config"]["cpu_allocation_ratio"] %> +<%- end %> +<%- if /RamFilter/.match(@scheduler_default_filters) %> +# virtual ram to physical ram allocation ratio (default: 1.5) +ram_allocation_ratio=<%= node["openstack"]["compute"]["config"]["ram_allocation_ratio"] %> +<%- end %> +<%- elsif /SimpleScheduler/.match(node["openstack"]["compute"]["scheduler"]["scheduler_driver"]) %> +# SimpleScheduler Only Options +# maximum number of instance cores to allow per host +max_cores=<%= node["openstack"]["compute"]["config"]["cpu_allocation_ratio"].to_i * node["cpu"]["total"].to_i %> +<%- end %> + +# If true, force creation of config drive regardless of if --config-drive was specified in the API call +force_config_drive=<%= node["openstack"]["compute"]["config"]["force_config_drive"] %> + +<% if %w(fedora redhat centos).include? node.platform -%> +# Adding support for non-modded euca2ools to display ip address info +# https://bugs.launchpad.net/nova/+bug/901594 +ec2_private_dns_show_ip=True +<% end -%> + +##### WORKERS ###### +ec2_workers=<%= node["cpu"]["total"] %> +osapi_compute_workers=<%= node["cpu"]["total"] %> +metadata_workers=<%= node["cpu"]["total"] %> + +##### KEYSTONE ##### +keystone_ec2_url=<%= @identity_endpoint.scheme %>://<%= @identity_endpoint.host %>:<%= @identity_endpoint.port %>/v2.0/ec2tokens + +##### VOLUMES ##### +# iscsi target user-land tool to use +iscsi_helper=<%= @iscsi_helper %> +volume_api_class=<%= node["openstack"]["compute"]["config"]["volume_api_class"] %> + +##### THIRD PARTY ADDITIONS ##### +<% if node["openstack"]["compute"]["plugins"] %> +<% node["openstack"]["compute"]["plugins"].each do |p| %> +osapi_compute_extension=<%= p %> +<% end %> +<% end %> + +[conductor] + +use_local=<%= node["openstack"]["compute"]["conductor"]["use_local"] %> diff --git a/chef/cookbooks/openstack-compute/templates/default/openrc.erb b/chef/cookbooks/openstack-compute/templates/default/openrc.erb new file mode 100644 index 0000000..1f59170 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/openrc.erb @@ -0,0 +1,23 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# COMMON OPENSTACK ENVS +export OS_USERNAME=<%= @user %> +export OS_PASSWORD=<%= @password %> +export OS_TENANT_NAME=<%= @tenant %> +export OS_AUTH_URL=<%= @identity_endpoint.to_s %> +export OS_AUTH_STRATEGY=<%= @auth_strategy %> +export OS_REGION_NAME=<%= node["openstack"]["compute"]["region"] %> + +# LEGACY NOVA ENVS +export NOVA_USERNAME=${OS_USERNAME} +export NOVA_PROJECT_ID=${OS_TENANT_NAME} +export NOVA_PASSWORD=${OS_PASSWORD} +export NOVA_API_KEY=${OS_PASSWORD} +export NOVA_URL=${OS_AUTH_URL} +export NOVA_VERSION=<%= @nova_api_version %> +export NOVA_REGION_NAME=<%= node["openstack"]["compute"]["region"] %> + +# EUCA2OOLs ENV VARIABLES +export EC2_ACCESS_KEY=<%= node["credentials"]["EC2"]["admin"]["access"] %> +export EC2_SECRET_KEY=<%= node["credentials"]["EC2"]["admin"]["secret"] %> +export EC2_URL=<%= @ec2_url %> diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.conf.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.conf.erb new file mode 100644 index 0000000..6d496c3 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/rootwrap.conf.erb @@ -0,0 +1,29 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# Configuration for nova-rootwrap +# This file should be owned by (and only-writeable by) the root user + +[DEFAULT] +# List of directories to load filter definitions from (separated by ','). +# These directories MUST all be only writeable by root ! +filters_path=/etc/nova/rootwrap.d,/usr/share/nova/rootwrap + +# List of directories to search executables in, in case filters do not +# explicitely specify a full path (separated by ',') +# If not specified, defaults to system PATH environment variable. +# These directories MUST all be only writeable by root ! +exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin + +# Enable logging to syslog +# Default value is False +use_syslog=False + +# Which syslog facility to use. +# Valid values include auth, authpriv, syslog, user0, user1... +# Default value is 'syslog' +syslog_log_facility=syslog + +# Which messages to log. +# INFO means log all usage +# ERROR means only log unsuccessful attempts +syslog_log_level=ERROR diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb new file mode 100644 index 0000000..cc623d2 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/api-metadata.filters.erb @@ -0,0 +1,15 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# nova-rootwrap command filters for api-metadata nodes +# This is needed on nova-api hosts running with "metadata" in enabled_apis +# or when running nova-api-metadata +# This file should be owned by (and only-writeable by) the root user + +[Filters] +# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... +iptables-save: CommandFilter, iptables-save, root +ip6tables-save: CommandFilter, ip6tables-save, root + +# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) +iptables-restore: CommandFilter, iptables-restore, root +ip6tables-restore: CommandFilter, ip6tables-restore, root diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb new file mode 100644 index 0000000..e9009bc --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/compute.filters.erb @@ -0,0 +1,203 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +# nova-rootwrap command filters for compute nodes +# This file should be owned by (and only-writeable by) the root user + +[Filters] +# nova/virt/disk/mount/api.py: 'kpartx', '-a', device +# nova/virt/disk/mount/api.py: 'kpartx', '-d', device +kpartx: CommandFilter, /sbin/kpartx, root + +# nova/virt/xenapi/vm_utils.py: tune2fs, -O ^has_journal, part_path +# nova/virt/xenapi/vm_utils.py: tune2fs, -j, partition_path +tune2fs: CommandFilter, /sbin/tune2fs, root + +# nova/virt/disk/mount/api.py: 'mount', mapped_device +# nova/virt/disk/api.py: 'mount', '-o', 'bind', src, target +# nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'.. +# nova/virt/configdrive.py: 'mount', device, mountdir +# nova/virt/libvirt/volume.py: 'mount', '-t', 'sofs' ... +mount: CommandFilter, /bin/mount, root + +# nova/virt/disk/mount/api.py: 'umount', mapped_device +# nova/virt/disk/api.py: 'umount' target +# nova/virt/xenapi/vm_utils.py: 'umount', dev_path +# nova/virt/configdrive.py: 'umount', mountdir +umount: CommandFilter, /bin/umount, root + +# nova/virt/disk/mount/nbd.py: 'qemu-nbd', '-c', device, image +# nova/virt/disk/mount/nbd.py: 'qemu-nbd', '-d', device +qemu-nbd: CommandFilter, /usr/bin/qemu-nbd, root + +# nova/virt/disk/mount/loop.py: 'losetup', '--find', '--show', image +# nova/virt/disk/mount/loop.py: 'losetup', '--detach', device +losetup: CommandFilter, /sbin/losetup, root + +# nova/virt/disk/vfs/localfs.py: 'tee', canonpath +tee: CommandFilter, /usr/bin/tee, root + +# nova/virt/disk/vfs/localfs.py: 'mkdir', canonpath +mkdir: CommandFilter, /bin/mkdir, root + +# nova/virt/disk/vfs/localfs.py: 'chown' +# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log +# nova/virt/libvirt/connection.py: 'chown', os.getuid( console_log +# nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk') +# nova/utils.py: 'chown', owner_uid, path +chown: CommandFilter, /bin/chown, root + +# nova/virt/disk/vfs/localfs.py: 'chmod' +chmod: CommandFilter, /bin/chmod, root + +# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap' +# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up' +# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev +# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i.. +# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'.. +# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',.. +# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',.. +# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev) +# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1] +# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge +# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', .. +# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',.. +# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ... +# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,.. +# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up' +# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up' +# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, .. +# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, .. +# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up' +# nova/network/linux_net.py: 'ip', 'route', 'add', .. +# nova/network/linux_net.py: 'ip', 'route', 'del', . +# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev +ip: CommandFilter, /sbin/ip, root + +# nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev +# nova/network/linux_net.py: 'tunctl', '-b', '-t', dev +tunctl: CommandFilter, tunctl, root + +# nova/virt/libvirt/vif.py: 'ovs-vsctl', ... +# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ... +# nova/network/linux_net.py: 'ovs-vsctl', .... +ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root + +# nova/network/linux_net.py: 'ovs-ofctl', .... +ovs-ofctl: CommandFilter, /usr/bin/ovs-ofctl, root + +# nova/virt/libvirt/connection.py: 'dd', if=%s % virsh_output, ... +dd: CommandFilter, /bin/dd, root + +# nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ... +iscsiadm: CommandFilter, iscsiadm, root + +# nova/virt/libvirt/volume.py: 'aoe-revalidate', aoedev +# nova/virt/libvirt/volume.py: 'aoe-discover' +aoe-revalidate: CommandFilter, /usr/sbin/aoe-revalidate, root +aoe-discover: CommandFilter, /usr/sbin/aoe-discover, root + +# nova/virt/xenapi/vm_utils.py: parted, --script, ... +# nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*. +parted: CommandFilter, parted, root + +# nova/virt/xenapi/vm_utils.py: 'pygrub', '-qn', dev_path +pygrub: CommandFilter, /usr/bin/pygrub, root + +# nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s +fdisk: CommandFilter, /sbin/fdisk, root + +# nova/virt/xenapi/vm_utils.py: e2fsck, -f, -p, partition_path +# nova/virt/disk/api.py: e2fsck, -f, -p, image +e2fsck: CommandFilter, /sbin/e2fsck, root + +# nova/virt/xenapi/vm_utils.py: resize2fs, partition_path +# nova/virt/disk/api.py: resize2fs, image +resize2fs: CommandFilter, /sbin/resize2fs, root + +# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... +iptables-save: CommandFilter, iptables-save, root +ip6tables-save: CommandFilter, ip6tables-save, root + +# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) +iptables-restore: CommandFilter, iptables-restore, root +ip6tables-restore: CommandFilter, ip6tables-restore, root + +# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ... +# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],.. +arping: CommandFilter, arping, root + +# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address +dhcp_release: CommandFilter, /usr/bin/dhcp_release, root + +# nova/network/linux_net.py: 'kill', '-9', pid +# nova/network/linux_net.py: 'kill', '-HUP', pid +kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP + +# nova/network/linux_net.py: 'kill', pid +kill_radvd: KillFilter, root, /usr/sbin/radvd + +# nova/network/linux_net.py: dnsmasq call +dnsmasq: DnsmasqFilter, /usr/sbin/dnsmasq, root +dnsmasq_deprecated: DeprecatedDnsmasqFilter, /usr/sbin/dnsmasq, root + +# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'.. +radvd: CommandFilter, /usr/sbin/radvd, root + +# nova/network/linux_net.py: 'brctl', 'addbr', bridge +# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0 +# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off' +# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface +brctl: CommandFilter, brctl, root + +# nova/virt/libvirt/utils.py: 'mkswap' +# nova/virt/xenapi/vm_utils.py: 'mkswap' +mkswap: CommandFilter, /sbin/mkswap, root + +# nova/virt/xenapi/vm_utils.py: 'mkfs' +mkfs: CommandFilter, /sbin/mkfs, root + +# nova/virt/libvirt/utils.py: 'qemu-img' +qemu-img: CommandFilter, /usr/bin/qemu-img, root + +# nova/virt/disk/vfs/localfs.py: 'readlink', '-e' +readlink: CommandFilter, readlink, root + +# nova/virt/disk/api.py: 'touch', target +touch: CommandFilter, /usr/bin/touch, root + +# nova/virt/disk/api.py: +mkfs.ext3: CommandFilter, /sbin/mkfs.ext3, root +mkfs.ntfs: CommandFilter, /sbin/mkfs.ntfs, root + +# nova/virt/libvirt/connection.py: +read_initiator: ReadFileFilter, /etc/iscsi/initiatorname.iscsi + +# nova/virt/libvirt/connection.py: +lvremove: CommandFilter, /sbin/lvremove, root + +# nova/virt/libvirt/utils.py: +lvcreate: CommandFilter, /sbin/lvcreate, root + +# nova/virt/libvirt/utils.py: +lvs: CommandFilter, /sbin/lvs, root + +# nova/virt/libvirt/utils.py: +vgs: CommandFilter, /sbin/vgs, root + +# nova/virt/baremetal/volume_driver.py: 'tgtadm', '--lld', 'iscsi', ... +tgtadm: CommandFilter, /usr/sbin/tgtadm, root + +# nova/utils.py:read_file_as_root: 'cat', file_path +# (called from nova/virt/disk/vfs/localfs.py:VFSLocalFS.read_file) +read_passwd: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/passwd +read_shadow: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/shadow + +# nova/virt/libvirt/volume.py: 'multipath' '-R' +multipath: CommandFilter, /sbin/multipath, root + +# nova/virt/libvirt/utils.py: +systool: CommandFilter, /usr/bin/systool, root + +# nova/virt/libvirt/volume.py: +sginfo: CommandFilter, /usr/bin/sginfo, root +sg_scan: CommandFilter, /usr/bin/sg_scan, root diff --git a/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb new file mode 100644 index 0000000..bb74ea2 --- /dev/null +++ b/chef/cookbooks/openstack-compute/templates/default/rootwrap.d/network.filters.erb @@ -0,0 +1,77 @@ +<%= node["openstack"]["compute"]["custom_template_banner"] %> + +[Filters] +# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap' +# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up' +# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev +# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i.. +# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'.. +# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',.. +# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',.. +# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev) +# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1] +# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge +# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', .. +# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',.. +# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ... +# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,.. +# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up' +# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up' +# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, .. +# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, .. +# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up' +# nova/network/linux_net.py: 'ip', 'route', 'add', .. +# nova/network/linux_net.py: 'ip', 'route', 'del', . +# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev +ip: CommandFilter, /sbin/ip, root + +# nova/virt/libvirt/vif.py: 'ovs-vsctl', ... +# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ... +# nova/network/linux_net.py: 'ovs-vsctl', .... +ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root + +# nova/network/linux_net.py: 'ovs-ofctl', .... +ovs-ofctl: CommandFilter, /usr/bin/ovs-ofctl, root + +# nova/network/linux_net.py: 'ebtables', '-D' ... +# nova/network/linux_net.py: 'ebtables', '-I' ... +ebtables: CommandFilter, /sbin/ebtables, root +ebtables_usr: CommandFilter, /usr/sbin/ebtables, root + +# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ... +iptables-save: CommandFilter, iptables-save, root +ip6tables-save: CommandFilter, ip6tables-save, root + +# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,) +iptables-restore: CommandFilter, iptables-restore, root +ip6tables-restore: CommandFilter, ip6tables-restore, root + +# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ... +# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],.. +arping: CommandFilter, arping, root + +# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address +dhcp_release: CommandFilter, /usr/bin/dhcp_release, root + +# nova/network/linux_net.py: 'kill', '-9', pid +# nova/network/linux_net.py: 'kill', '-HUP', pid +kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP + +# nova/network/linux_net.py: 'kill', pid +kill_radvd: KillFilter, root, /usr/sbin/radvd + +# nova/network/linux_net.py: dnsmasq call +dnsmasq: DnsmasqFilter, /usr/sbin/dnsmasq, root +dnsmasq_deprecated: DeprecatedDnsmasqFilter, /usr/sbin/dnsmasq, root + +# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'.. +radvd: CommandFilter, /usr/sbin/radvd, root + +# nova/network/linux_net.py: 'brctl', 'addbr', bridge +# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0 +# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off' +# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface +brctl: CommandFilter, brctl, root + +# nova/network/linux_net.py: 'sysctl', .... +sysctl: CommandFilter, /sbin/sysctl, root diff --git a/chef/cookbooks/openstack-dashboard/.tailor b/chef/cookbooks/openstack-dashboard/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-dashboard/Berksfile b/chef/cookbooks/openstack-dashboard/Berksfile new file mode 100644 index 0000000..84e5b6d --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/Berksfile @@ -0,0 +1,4 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-dashboard/Gemfile b/chef/cookbooks/openstack-dashboard/Gemfile new file mode 100644 index 0000000..ffbff4a --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 1.4.5" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-dashboard/Gemfile.lock b/chef/cookbooks/openstack-dashboard/Gemfile.lock new file mode 100644 index 0000000..fb9593e --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/Gemfile.lock @@ -0,0 +1,207 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.13) + i18n (= 0.6.1) + multi_json (~> 1.0) + addressable (2.3.4) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (1.4.5) + activesupport (>= 3.2.0) + addressable + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + json (>= 1.5.0) + minitar + mixlib-config (~> 1.1) + mixlib-shellout (~> 1.1) + multi_json (~> 1.5) + retryable + ridley (~> 0.12.4) + solve (>= 0.4.2) + thor (~> 0.18.0) + yajl-ruby + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.0) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.8.4) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.8.1) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.1) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.4) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.6) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rak (1.4) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (0.12.4) + addressable + celluloid (~> 0.14.0) + chozo (>= 0.6.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + mixlib-authentication (>= 1.3.0) + mixlib-config (>= 1.1.0) + mixlib-log (>= 1.3.0) + mixlib-shellout (>= 1.1.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + winrm (~> 1.1.0) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.4.4) + json + strainer (2.1.0) + berkshelf (~> 1.3) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.0) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 1.4.5) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-dashboard/README.md b/chef/cookbooks/openstack-dashboard/README.md new file mode 100644 index 0000000..5144dd5 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/README.md @@ -0,0 +1,93 @@ +Description +=========== + +Installs the OpenStack Dashboard service **Horizon** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Horizon is currently installed from packages. + +http://horizon.openstack.org + +Requirements +============ + +* Chef 0.10.0 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* apache2 +* openstack-common + +Usage +===== + +server +------ + +Sets up the Horizon dashboard within an Apache `mod_wsgi` container. + +```json +"run_list": [ + "recipe[openstack-dashboard::server]" +] +``` + +Attributes +========== + +* `openstack["dashboard"]["db"]["username"]` - username for horizon database access +* `openstack["dashboard"]["server_hostname"]` - sets the ServerName in the Apache config. +* `openstack["dashboard"]["use_ssl"]` - toggle for using ssl with dashboard (default true) +* `openstack["dashboard"]["ssl"]["dir"]` - directory where ssl certs are stored on this system +* `openstack["dashboard"]["ssl"]["cert"]` - name to use when creating the ssl certificate +* `openstack["dashboard"]["ssl"]["key"]` - name to use when creating the ssl key +* `openstack["dashboard"]["dash_path"]` - base path for dashboard files (document root) +* `openstack["dashboard"]["wsgi_path"]` - path for wsgi dir +* `openstack["dashboard"]["ssl_offload"]` - Set SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') flag for offloading SSL +* `openstack["dashboard"]["plugins"]` - Array of plugins to include via INSTALED\_APPS + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Justin Shepherd () | +| **Author** | Jason Cannavale () | +| **Author** | Ron Pedde () | +| **Author** | Joseph Breu () | +| **Author** | William Kelly () | +| **Author** | Darren Birkett () | +| **Author** | Evan Callicoat () | +| **Author** | Jay Pipes () | +| **Author** | John Dewey () | +| **Author** | Matt Ray () | +| **Author** | Sean Gallagher () | +| | | +| **Copyright** | Copyright (c) 2012, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2012-2013, AT&T Services, Inc. | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-dashboard/Strainerfile b/chef/cookbooks/openstack-dashboard/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-dashboard/attributes/default.rb b/chef/cookbooks/openstack-dashboard/attributes/default.rb new file mode 100644 index 0000000..aacb04b --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/attributes/default.rb @@ -0,0 +1,104 @@ +# +# Cookbook Name:: openstack-dashboard +# Attributes:: default +# +# Copyright 2012, AT&T, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["dashboard"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +default["openstack"]["dashboard"]["debug"] = false + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# db_password routine. +default["openstack"]["dashboard"]["db"]["username"] = "dash" + +# The Keystone role used by default for users logging into the dashboard +default["openstack"]["dashboard"]["keystone_default_role"] = "Member" + +# This is the name of the Chef role that will install the Keystone Service API +default["openstack"]["dashboard"]["keystone_service_chef_role"] = "keystone" + +default["openstack"]["dashboard"]["server_hostname"] = nil +default["openstack"]["dashboard"]["use_ssl"] = true +default["openstack"]["dashboard"]["ssl"]["cert"] = "horizon.pem" +default["openstack"]["dashboard"]["ssl"]["key"] = "horizon.key" + +default["openstack"]["dashboard"]["swift"]["enabled"] = "False" + +default["openstack"]["dashboard"]["theme"] = "default" + +default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/openstack-dashboard" + +case node["platform"] +when "fedora", "centos", "redhat" + default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/pki/tls" + default["openstack"]["dashboard"]["local_settings_path"] = "/etc/openstack-dashboard/local_settings" + default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/static" + # TODO(shep) - Fedora does not generate self signed certs by default + default["openstack"]["dashboard"]["platform"] = { + "mysql_python_packages" => ["MySQL-python"], + "postgresql_python_packages" => ["python-psycopg2"], + "horizon_packages" => ["openstack-dashboard"], + "memcache_python_packages" => ["python-memcached"], + "package_overrides" => "" + } + if node["platform"] == "fedora" + default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" + else + default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard" + end +when "suse" + default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/ssl" + default["openstack"]["dashboard"]["local_settings_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py" + default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/static" + default["openstack"]["dashboard"]["platform"] = { + "mysql_python_packages" => ["python-mysql"], + "postgresql_python_packages" => ["python-psycopg2"], + "horizon_packages" => ["openstack-dashboard"], + "memcache_python_packages" => ["python-python-memcached"], + "package_overrides" => "" + } + default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" +when "ubuntu" + default["openstack"]["dashboard"]["ssl"]["dir"] = "/etc/ssl" + default["openstack"]["dashboard"]["local_settings_path"] = "/etc/openstack-dashboard/local_settings.py" + default["openstack"]["dashboard"]["static_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/static" + default["openstack"]["dashboard"]["platform"] = { + "horizon_packages" => ["lessc", "openstack-dashboard"], + "mysql_python_packages" => ["python-mysqldb"], + "postgresql_python_packages" => ["python-psycopg2"], + "memcache_python_packages" => ["python-memcache"], + "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } + default["openstack"]["dashboard"]["apache"]["sites-path"] = "#{node["apache"]["dir"]}/sites-available/openstack-dashboard" +end + +default["openstack"]["dashboard"]["dash_path"] = "/usr/share/openstack-dashboard/openstack_dashboard" +default["openstack"]["dashboard"]["stylesheet_path"] = "/usr/share/openstack-dashboard/openstack_dashboard/templates/_stylesheets.html" +default["openstack"]["dashboard"]["wsgi_path"] = node["openstack"]["dashboard"]["dash_path"] + "/wsgi/django.wsgi" +default["openstack"]["dashboard"]["session_backend"] = "memcached" + +default["openstack"]["dashboard"]["ssl_offload"] = false +default["openstack"]["dashboard"]["plugins"] = nil + +default["openstack"]["dashboard"]["error_log"] = "openstack-dashboard-error.log" +default["openstack"]["dashboard"]["access_log"] = "openstack-dashboard-access.log" diff --git a/chef/cookbooks/openstack-dashboard/files/default/css/folsom.css b/chef/cookbooks/openstack-dashboard/files/default/css/folsom.css new file mode 100644 index 0000000..12e8c67 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/files/default/css/folsom.css @@ -0,0 +1,6363 @@ +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section +{ + display: block; +} + +audio,canvas,video +{ + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) +{ + display: none; +} + +html +{ + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + background-color: #ddd; +} + +a:focus +{ + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover,a:active +{ + outline: 0; +} + +sub,sup +{ + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup +{ + top: -0.5em; +} + +sub +{ + bottom: -0.25em; +} + +img +{ + max-width: 100%; + height: auto; + border: 0; + -ms-interpolation-mode: bicubic; +} + +button,input,select,textarea +{ + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button,input +{ + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner,input::-moz-focus-inner +{ + padding: 0; + border: 0; +} + +button,input[type="button"],input[type="reset"],input[type="submit"] +{ + cursor: pointer; + -webkit-appearance: button; +} + +input[type="search"] +{ + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button +{ + -webkit-appearance: none; +} + +textarea +{ + overflow: auto; + vertical-align: top; +} + +.clearfix +{ + *zoom: 1; +} + +.clearfix:before,.clearfix:after +{ + display: table; + content: ""; +} + +.clearfix:after +{ + clear: both; +} + +body +{ + margin: 0; + font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; + font-size: 13px; + line-height: 18px; + color: #333333; + background-color: #ffffff; +} + +a +{ + color: #1d71bf; + text-decoration: none; +} + +a:hover +{ + color: #1d71bf; + text-decoration: underline; +} + +.row +{ + margin-left: -20px; + *zoom: 1; +} + +.row:before,.row:after +{ + display: table; + content: ""; +} + +.row:after +{ + clear: both; +} + +[class*="span"] +{ + float: left; + margin-left: 20px; +} + +.span1 +{ + width: 60px; +} + +.span2 +{ + width: 140px; +} + +.span3 +{ + width: 220px; +} + +.span4 +{ + width: 300px; +} + +.span5 +{ + width: 380px; +} + +.span6 +{ + width: 460px; +} + +.span7 +{ + width: 540px; +} + +.span8 +{ + width: 620px; +} + +.span9 +{ + width: 700px; +} + +.span10 +{ + width: 780px; +} + +.span11 +{ + width: 860px; +} + +.span12,.container +{ + width: 940px; +} + +.offset1 +{ + margin-left: 100px; +} + +.offset2 +{ + margin-left: 180px; +} + +.offset3 +{ + margin-left: 260px; +} + +.offset4 +{ + margin-left: 340px; +} + +.offset5 +{ + margin-left: 420px; +} + +.offset6 +{ + margin-left: 500px; +} + +.offset7 +{ + margin-left: 580px; +} + +.offset8 +{ + margin-left: 660px; +} + +.offset9 +{ + margin-left: 740px; +} + +.offset10 +{ + margin-left: 820px; +} + +.offset11 +{ + margin-left: 900px; +} + +.row-fluid +{ + width: 100%; + *zoom: 1; +} + +.row-fluid:before,.row-fluid:after +{ + display: table; + content: ""; +} + +.row-fluid:after +{ + clear: both; +} + +.row-fluid>[class*="span"] +{ + float: left; + margin-left: 2.127659574%; +} + +.row-fluid>[class*="span"]:first-child +{ + margin-left: 0; +} + +.row-fluid>.span1 +{ + width: 6.382978723%; +} + +.row-fluid>.span2 +{ + width: 14.89361702%; +} + +.row-fluid>.span3 +{ + width: 23.404255317%; +} + +.row-fluid>.span4 +{ + width: 31.914893614%; +} + +.row-fluid>.span5 +{ + width: 40.425531911%; +} + +.row-fluid>.span6 +{ + width: 48.93617020799999%; +} + +.row-fluid>.span7 +{ + width: 57.446808505%; +} + +.row-fluid>.span8 +{ + width: 65.95744680199999%; +} + +.row-fluid>.span9 +{ + width: 74.468085099%; +} + +.row-fluid>.span10 +{ + width: 82.97872339599999%; +} + +.row-fluid>.span11 +{ + width: 91.489361693%; +} + +.row-fluid>.span12 +{ + width: 99.99999998999999%; +} + +.container +{ + width: 940px; + margin-left: auto; + margin-right: auto; + *zoom: 1; +} + +.container:before,.container:after +{ + display: table; + content: ""; +} + +.container:after +{ + clear: both; +} + +.container-fluid +{ + padding-left: 20px; + padding-right: 20px; + *zoom: 1; +} + +.container-fluid:before,.container-fluid:after +{ + display: table; + content: ""; +} + +.container-fluid:after +{ + clear: both; +} + +p +{ + margin: 0 0 9px; + font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; + font-size: 13px; + line-height: 18px; +} + +p small +{ + font-size: 11px; + color: #999999; +} + +.lead +{ + margin-bottom: 18px; + font-size: 20px; + font-weight: 200; + line-height: 27px; +} + +h1,h2,h3,h4,h5,h6 +{ + margin: 0; + font-weight: bold; + color: #333333; + text-rendering: optimizelegibility; +} + +h1 small,h2 small,h3 small,h4 small,h5 small,h6 small +{ + font-weight: normal; + color: #999999; +} + +h1 +{ + font-size: 30px; + line-height: 36px; +} + +h1 small +{ + font-size: 18px; +} + +h2 +{ + font-size: 24px; + line-height: 36px; +} + +h2 small +{ + font-size: 18px; +} + +h3 +{ + line-height: 27px; + font-size: 18px; +} + +h3 small +{ + font-size: 14px; +} + +h4,h5,h6 +{ + line-height: 18px; +} + +h4 +{ + font-size: 14px; +} + +h4 small +{ + font-size: 12px; +} + +h5 +{ + font-size: 12px; +} + +h6 +{ + font-size: 11px; + color: #999999; + text-transform: uppercase; +} + +.page-header +{ + padding-bottom: 17px; + margin: 18px 0; + border-bottom: 1px solid #eeeeee; +} + +.page-header h1 +{ + line-height: 1; +} + +ul,ol +{ + padding: 0; + margin: 0 0 9px 25px; +} + +ul ul,ul ol,ol ol,ol ul +{ + margin-bottom: 0; +} + +ul +{ + list-style: disc; +} + +ol +{ + list-style: decimal; +} + +li +{ + line-height: 18px; +} + +ul.unstyled,ol.unstyled +{ + margin-left: 0; + list-style: none; +} + +dl +{ + margin-bottom: 18px; +} + +dt,dd +{ + line-height: 18px; +} + +dt +{ + font-weight: bold; +} + +dd +{ + margin-left: 9px; +} + +hr +{ + margin: 18px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +strong +{ + font-weight: bold; +} + +em +{ + font-style: italic; +} + +.muted +{ + color: #999999; +} + +abbr +{ + font-size: 90%; + text-transform: uppercase; + border-bottom: 1px dotted #ddd; + cursor: help; +} + +blockquote +{ + padding: 0 0 0 15px; + margin: 0 0 18px; + border-left: 5px solid #eeeeee; +} + +blockquote p +{ + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 22.5px; +} + +blockquote small +{ + display: block; + line-height: 18px; + color: #999999; +} + +blockquote small:before +{ + content: '\2014 \00A0'; +} + +blockquote.pull-right +{ + float: right; + padding-left: 0; + padding-right: 15px; + border-left: 0; + border-right: 5px solid #eeeeee; +} + +blockquote.pull-right p,blockquote.pull-right small +{ + text-align: right; +} + +q:before,q:after,blockquote:before,blockquote:after +{ + content: ""; +} + +address +{ + display: block; + margin-bottom: 18px; + line-height: 18px; + font-style: normal; +} + +small +{ + font-size: 100%; +} + +cite +{ + font-style: normal; +} + +code,pre +{ + padding: 0 3px 2px; + font-family: Menlo,Monaco,"Courier New",monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code +{ + padding: 3px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre +{ + display: block; + padding: 8.5px; + margin: 0 0 9px; + font-size: 12px; + line-height: 18px; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + white-space: pre; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; +} + +pre.prettyprint +{ + margin-bottom: 18px; +} + +pre code +{ + padding: 0; + color: inherit; + background-color: transparent; + border: 0; +} + +.pre-scrollable +{ + max-height: 340px; + overflow-y: scroll; +} + +form +{ + margin: 0px; +} + +fieldset +{ + padding: 0; + margin: 0; + border: 0; +} + +legend +{ + display: block; + width: 100%; + padding: 0; + margin-bottom: 27px; + font-size: 19.5px; + line-height: 36px; + color: #333333; + border: 0; + border-bottom: 1px solid #eee; +} + +legend small +{ + font-size: 13.5px; + color: #999999; +} + +label,input,button,select,textarea +{ + font-size: 13px; + font-weight: normal; + line-height: 18px; +} + +input,button,select,textarea +{ + font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; +} + +label +{ + display: block; + margin-bottom: 5px; + color: #333333; +} + +input,textarea,select,.uneditable-input +{ + display: inline-block; + width: 210px; + height: 18px; + padding: 4px; + margin-bottom: 9px; + font-size: 13px; + line-height: 18px; + color: #555555; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.uneditable-textarea +{ + width: auto; + height: auto; +} + +label input,label textarea,label select +{ + display: block; +} + +input[type="image"],input[type="checkbox"],input[type="radio"] +{ + width: auto; + height: auto; + padding: 0; + margin: 3px 0; + *margin-top: 0; + line-height: normal; + cursor: pointer; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + border: 0 \9; +} + +input[type="image"] +{ + border: 0; +} + +input[type="file"] +{ + width: auto; + padding: initial; + line-height: initial; + border: initial; + background-color: #ffffff; + background-color: initial; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +input[type="button"],input[type="reset"],input[type="submit"] +{ + width: auto; + height: auto; +} + +select,input[type="file"] +{ + height: 28px; + *margin-top: 4px; + line-height: 28px; +} + +input[type="file"] +{ + line-height: 18px \9; +} + +select +{ + width: 220px; + background-color: #ffffff; +} + +select[multiple],select[size] +{ + height: auto; +} + +input[type="image"] +{ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +textarea +{ + height: auto; +} + +input[type="hidden"] +{ + display: none; +} + +.radio,.checkbox +{ + padding-left: 18px; +} + +.radio input[type="radio"],.checkbox input[type="checkbox"] +{ + float: left; + margin-left: -18px; +} + +.controls>.radio:first-child,.controls>.checkbox:first-child +{ + padding-top: 5px; +} + +.radio.inline,.checkbox.inline +{ + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline +{ + margin-left: 10px; +} + +input,textarea +{ + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s,box-shadow linear 0.2s; + -moz-transition: border linear 0.2s,box-shadow linear 0.2s; + -ms-transition: border linear 0.2s,box-shadow linear 0.2s; + -o-transition: border linear 0.2s,box-shadow linear 0.2s; + transition: border linear 0.2s,box-shadow linear 0.2s; +} + +input:focus,textarea:focus +{ + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6); + outline: 0; + outline: thin dotted \9; +} + +input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus +{ + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.input-mini +{ + width: 60px; +} + +.input-small +{ + width: 90px; +} + +.input-medium +{ + width: 150px; +} + +.input-large +{ + width: 210px; +} + +.input-xlarge +{ + width: 270px; +} + +.input-xxlarge +{ + width: 530px; +} + +input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input +{ + float: none; + margin-left: 0; +} + +input.span1,textarea.span1,.uneditable-input.span1 +{ + width: 50px; +} + +input.span2,textarea.span2,.uneditable-input.span2 +{ + width: 130px; +} + +input.span3,textarea.span3,.uneditable-input.span3 +{ + width: 210px; +} + +input.span4,textarea.span4,.uneditable-input.span4 +{ + width: 290px; +} + +input.span5,textarea.span5,.uneditable-input.span5 +{ + width: 370px; +} + +input.span6,textarea.span6,.uneditable-input.span6 +{ + width: 450px; +} + +input.span7,textarea.span7,.uneditable-input.span7 +{ + width: 530px; +} + +input.span8,textarea.span8,.uneditable-input.span8 +{ + width: 610px; +} + +input.span9,textarea.span9,.uneditable-input.span9 +{ + width: 690px; +} + +input.span10,textarea.span10,.uneditable-input.span10 +{ + width: 770px; +} + +input.span11,textarea.span11,.uneditable-input.span11 +{ + width: 850px; +} + +input.span12,textarea.span12,.uneditable-input.span12 +{ + width: 930px; +} + +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly] +{ + background-color: #f5f5f5; + border-color: #ddd; + cursor: not-allowed; +} + +.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline +{ + color: #c09853; +} + +.control-group.warning input,.control-group.warning select,.control-group.warning textarea +{ + color: #c09853; + border-color: #c09853; +} + +.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus +{ + border-color: #a47e3c; + -webkit-box-shadow: 0 0 6px #dbc59e; + -moz-box-shadow: 0 0 6px #dbc59e; + box-shadow: 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on +{ + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline +{ + color: #c40022; +} + +.control-group.error input,.control-group.error select,.control-group.error textarea +{ + color: #333; + border-color: #c40022; +} + +.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus +{ + border-color: #953b39; + -webkit-box-shadow: 0 0 6px #d59392; + -moz-box-shadow: 0 0 6px #d59392; + box-shadow: 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on +{ + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline +{ + color: #468847; +} + +.control-group.success input,.control-group.success select,.control-group.success textarea +{ + color: #468847; + border-color: #468847; +} + +.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus +{ + border-color: #356635; + -webkit-box-shadow: 0 0 6px #7aba7b; + -moz-box-shadow: 0 0 6px #7aba7b; + box-shadow: 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on +{ + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid +{ + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus +{ + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions +{ + padding: 17px 20px 18px; + margin-top: 18px; + margin-bottom: 18px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; +} + +.uneditable-input +{ + display: block; + background-color: #ffffff; + border-color: #eee; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} + +:-moz-placeholder +{ + color: #999999; +} + +::-webkit-input-placeholder +{ + color: #999999; +} + +.help-block +{ + display: block; + margin-top: 5px; + margin-bottom: 0; + color: #999999; +} + +.help-inline +{ + display: inline-block; + *display: inline; + *zoom: 1; + margin-bottom: 9px; + vertical-align: middle; + padding-left: 5px; +} + +.input-prepend,.input-append +{ + margin-bottom: 5px; + *zoom: 1; +} + +.input-prepend:before,.input-append:before,.input-prepend:after,.input-append:after +{ + display: table; + content: ""; +} + +.input-prepend:after,.input-append:after +{ + clear: both; +} + +.input-prepend input,.input-append input,.input-prepend .uneditable-input,.input-append .uneditable-input +{ + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} + +.input-prepend input:focus,.input-append input:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus +{ + position: relative; + z-index: 2; +} + +.input-prepend .uneditable-input,.input-append .uneditable-input +{ + border-left-color: #ccc; +} + +.input-prepend .add-on,.input-append .add-on +{ + float: left; + display: block; + width: auto; + min-width: 16px; + height: 18px; + margin-right: -1px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + color: #999999; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #f5f5f5; + border: 1px solid #ccc; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} + +.input-prepend .active,.input-append .active +{ + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on +{ + *margin-top: 1px; +} + +.input-append input,.input-append .uneditable-input +{ + float: left; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} + +.input-append .uneditable-input +{ + border-left-color: #eee; + border-right-color: #ccc; +} + +.input-append .add-on +{ + margin-right: 0; + margin-left: -1px; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} + +.input-append input:first-child +{ + *margin-left: -160px; +} + +.input-append input:first-child+.add-on +{ + *margin-left: -21px; +} + +.search-query +{ + padding-left: 14px; + padding-right: 14px; + margin-bottom: 0; + -webkit-border-radius: 14px; + -moz-border-radius: 14px; + border-radius: 14px; +} + +.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input +{ + display: inline-block; + margin-bottom: 0; +} + +.form-search .hide,.form-inline .hide,.form-horizontal .hide +{ + display: none; +} + +.form-search label,.form-inline label,.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend +{ + display: inline-block; +} + +.form-search .input-append .add-on,.form-inline .input-prepend .add-on,.form-search .input-append .add-on,.form-inline .input-prepend .add-on +{ + vertical-align: middle; +} + +.form-search .radio,.form-inline .radio,.form-search .checkbox,.form-inline .checkbox +{ + margin-bottom: 0; + vertical-align: middle; +} + +.control-group +{ + margin-bottom: 9px; +} + +legend+.control-group +{ + margin-top: 18px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group +{ + margin-bottom: 18px; + *zoom: 1; +} + +.form-horizontal .control-group:before,.form-horizontal .control-group:after +{ + display: table; + content: ""; +} + +.form-horizontal .control-group:after +{ + clear: both; +} + +.form-horizontal .control-label +{ + float: left; + width: 140px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls +{ + margin-left: 160px; +} + +.form-horizontal .form-actions +{ + padding-left: 160px; +} + +table +{ + max-width: 100%; + border-collapse: collapse; + border-spacing: 0; +} + +.table +{ + width: 100%; + margin-bottom: 18px; +} + +.table th,.table td +{ + padding: 10px; + line-height: 20px; + text-align: left; + border-top: 1px solid #ddd; + color: #333; +} + +.table tbody td +{ + border-left: none !important; + background-color: #fff; +} + +.table th +{ + font-weight: normal; + color: #aaa; + padding: 0px 10px; + background-color: #fff; + line-height: 20px; + +} + +.table thead th +{ + vertical-align: middle; + border-left: none; + white-space: nowrap; +} + +.table thead:first-child tr th,.table thead:first-child tr td +{ + border-top: 0; +} + +.table tbody+tbody +{ + border-top: 2px solid #ddd; +} + +.table-condensed th,.table-condensed td +{ + padding: 4px 5px; +} + +.table-bordered +{ + border: 1px solid #ddd; + border-collapse: separate; + *border-collapse: collapsed; +} + +.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th +{ + +} + +.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td +{ + border-top: 0; +} + +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th +{ + background-color: #f9f9f9; +} + +.table tbody tr:hover td,.table tbody tr:hover th +{ + background-color: #f5f5f5; +} + +table .span1 +{ + float: none; + width: 44px; + margin-left: 0; +} + +table .span2 +{ + float: none; + width: 124px; + margin-left: 0; +} + +table .span3 +{ + float: none; + width: 204px; + margin-left: 0; +} + +table .span4 +{ + float: none; + width: 284px; + margin-left: 0; +} + +table .span5 +{ + float: none; + width: 364px; + margin-left: 0; +} + +table .span6 +{ + float: none; + width: 444px; + margin-left: 0; +} + +table .span7 +{ + float: none; + width: 524px; + margin-left: 0; +} + +table .span8 +{ + float: none; + width: 604px; + margin-left: 0; +} + +table .span9 +{ + float: none; + width: 684px; + margin-left: 0; +} + +table .span10 +{ + float: none; + width: 764px; + margin-left: 0; +} + +table .span11 +{ + float: none; + width: 844px; + margin-left: 0; +} + +table .span12 +{ + float: none; + width: 924px; + margin-left: 0; +} + +[class^="icon-"],[class*=" icon-"] +{ + display: inline-block; + width: 14px; + height: 14px; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + *margin-right: .3em; +} + +[class^="icon-"]:last-child,[class*=" icon-"]:last-child +{ + *margin-left: 0; +} + +.icon-white +{ + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass +{ + background-position: 0 0; +} + +.icon-music +{ + background-position: -24px 0; +} + +.icon-search +{ + background-position: -48px 0; +} + +.icon-envelope +{ + background-position: -72px 0; +} + +.icon-heart +{ + background-position: -96px 0; +} + +.icon-star +{ + background-position: -120px 0; +} + +.icon-star-empty +{ + background-position: -144px 0; +} + +.icon-user +{ + background-position: -168px 0; +} + +.icon-film +{ + background-position: -192px 0; +} + +.icon-th-large +{ + background-position: -216px 0; +} + +.icon-th +{ + background-position: -240px 0; +} + +.icon-th-list +{ + background-position: -264px 0; +} + +.icon-ok +{ + background-position: -288px 0; +} + +.icon-remove +{ + background-position: -312px 0; +} + +.icon-zoom-in +{ + background-position: -336px 0; +} + +.icon-zoom-out +{ + background-position: -360px 0; +} + +.icon-off +{ + background-position: -384px 0; +} + +.icon-signal +{ + background-position: -408px 0; +} + +.icon-cog +{ + background-position: -432px 0; +} + +.icon-trash +{ + background-position: -456px 0; +} + +.icon-home +{ + background-position: 0 -24px; +} + +.icon-file +{ + background-position: -24px -24px; +} + +.icon-time +{ + background-position: -48px -24px; +} + +.icon-road +{ + background-position: -72px -24px; +} + +.icon-download-alt +{ + background-position: -96px -24px; +} + +.icon-download +{ + background-position: -120px -24px; +} + +.icon-upload +{ + background-position: -144px -24px; +} + +.icon-inbox +{ + background-position: -168px -24px; +} + +.icon-play-circle +{ + background-position: -192px -24px; +} + +.icon-repeat +{ + background-position: -216px -24px; +} + +.icon-refresh +{ + background-position: -240px -24px; +} + +.icon-list-alt +{ + background-position: -264px -24px; +} + +.icon-lock +{ + background-position: -287px -24px; +} + +.icon-flag +{ + background-position: -312px -24px; +} + +.icon-headphones +{ + background-position: -336px -24px; +} + +.icon-volume-off +{ + background-position: -360px -24px; +} + +.icon-volume-down +{ + background-position: -384px -24px; +} + +.icon-volume-up +{ + background-position: -408px -24px; +} + +.icon-qrcode +{ + background-position: -432px -24px; +} + +.icon-barcode +{ + background-position: -456px -24px; +} + +.icon-tag +{ + background-position: 0 -48px; +} + +.icon-tags +{ + background-position: -25px -48px; +} + +.icon-book +{ + background-position: -48px -48px; +} + +.icon-bookmark +{ + background-position: -72px -48px; +} + +.icon-print +{ + background-position: -96px -48px; +} + +.icon-camera +{ + background-position: -120px -48px; +} + +.icon-font +{ + background-position: -144px -48px; +} + +.icon-bold +{ + background-position: -167px -48px; +} + +.icon-italic +{ + background-position: -192px -48px; +} + +.icon-text-height +{ + background-position: -216px -48px; +} + +.icon-text-width +{ + background-position: -240px -48px; +} + +.icon-align-left +{ + background-position: -264px -48px; +} + +.icon-align-center +{ + background-position: -288px -48px; +} + +.icon-align-right +{ + background-position: -312px -48px; +} + +.icon-align-justify +{ + background-position: -336px -48px; +} + +.icon-list +{ + background-position: -360px -48px; +} + +.icon-indent-left +{ + background-position: -384px -48px; +} + +.icon-indent-right +{ + background-position: -408px -48px; +} + +.icon-facetime-video +{ + background-position: -432px -48px; +} + +.icon-picture +{ + background-position: -456px -48px; +} + +.icon-pencil +{ + background-position: 0 -72px; +} + +.icon-map-marker +{ + background-position: -24px -72px; +} + +.icon-adjust +{ + background-position: -48px -72px; +} + +.icon-tint +{ + background-position: -72px -72px; +} + +.icon-edit +{ + background-position: -96px -72px; +} + +.icon-share +{ + background-position: -120px -72px; +} + +.icon-check +{ + background-position: -144px -72px; +} + +.icon-move +{ + background-position: -168px -72px; +} + +.icon-step-backward +{ + background-position: -192px -72px; +} + +.icon-fast-backward +{ + background-position: -216px -72px; +} + +.icon-backward +{ + background-position: -240px -72px; +} + +.icon-play +{ + background-position: -264px -72px; +} + +.icon-pause +{ + background-position: -288px -72px; +} + +.icon-stop +{ + background-position: -312px -72px; +} + +.icon-forward +{ + background-position: -336px -72px; +} + +.icon-fast-forward +{ + background-position: -360px -72px; +} + +.icon-step-forward +{ + background-position: -384px -72px; +} + +.icon-eject +{ + background-position: -408px -72px; +} + +.icon-chevron-left +{ + background-position: -432px -72px; +} + +.icon-chevron-right +{ + background-position: -456px -72px; +} + +.icon-plus-sign +{ + background-position: 0 -96px; +} + +.icon-minus-sign +{ + background-position: -24px -96px; +} + +.icon-remove-sign +{ + background-position: -48px -96px; +} + +.icon-ok-sign +{ + background-position: -72px -96px; +} + +.icon-question-sign +{ + background-position: -96px -96px; +} + +.icon-info-sign +{ + background-position: -120px -96px; +} + +.icon-screenshot +{ + background-position: -144px -96px; +} + +.icon-remove-circle +{ + background-position: -168px -96px; +} + +.icon-ok-circle +{ + background-position: -192px -96px; +} + +.icon-ban-circle +{ + background-position: -216px -96px; +} + +.icon-arrow-left +{ + background-position: -240px -96px; +} + +.icon-arrow-right +{ + background-position: -264px -96px; +} + +.icon-arrow-up +{ + background-position: -289px -96px; +} + +.icon-arrow-down +{ + background-position: -312px -96px; +} + +.icon-share-alt +{ + background-position: -336px -96px; +} + +.icon-resize-full +{ + background-position: -360px -96px; +} + +.icon-resize-small +{ + background-position: -384px -96px; +} + +.icon-plus +{ + background-position: -408px -96px; +} + +.icon-minus +{ + background-position: -433px -96px; +} + +.icon-asterisk +{ + background-position: -456px -96px; +} + +.icon-exclamation-sign +{ + background-position: 0 -120px; +} + +.icon-gift +{ + background-position: -24px -120px; +} + +.icon-leaf +{ + background-position: -48px -120px; +} + +.icon-fire +{ + background-position: -72px -120px; +} + +.icon-eye-open +{ + background-position: -96px -120px; +} + +.icon-eye-close +{ + background-position: -120px -120px; +} + +.icon-warning-sign +{ + background-position: -144px -120px; +} + +.icon-plane +{ + background-position: -168px -120px; +} + +.icon-calendar +{ + background-position: -192px -120px; +} + +.icon-random +{ + background-position: -216px -120px; +} + +.icon-comment +{ + background-position: -240px -120px; +} + +.icon-magnet +{ + background-position: -264px -120px; +} + +.icon-chevron-up +{ + background-position: -288px -120px; +} + +.icon-chevron-down +{ + background-position: -313px -119px; +} + +.icon-retweet +{ + background-position: -336px -120px; +} + +.icon-shopping-cart +{ + background-position: -360px -120px; +} + +.icon-folder-close +{ + background-position: -384px -120px; +} + +.icon-folder-open +{ + background-position: -408px -120px; +} + +.icon-resize-vertical +{ + background-position: -432px -119px; +} + +.icon-resize-horizontal +{ + background-position: -456px -118px; +} + +.dropdown +{ + position: relative; +} + +.dropdown-toggle +{ + *margin-bottom: -3px; +} + +.dropdown-toggle:active,.open .dropdown-toggle +{ + outline: 0; +} + +.caret +{ + display: inline-block; + width: 0; + height: 0; + text-indent: -99999px; + *text-indent: 0; + vertical-align: top; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #000000; + opacity: 0.3; + filter: alpha(opacity=30); + content: "\2193"; +} + +.dropdown .caret +{ + margin-top: 8px; + margin-left: 2px; +} + +.dropdown:hover .caret,.open.dropdown .caret +{ + opacity: 1; + filter: alpha(opacity=100); +} + +.dropdown-menu +{ + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + float: left; + display: none; + min-width: 160px; + _width: 160px; + padding: 4px 0; + margin: 0; + list-style: none; + background-color: #ffffff; + border-color: #ccc; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 1px; + -webkit-border-radius: 0 0 5px 5px; + -moz-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + *border-right-width: 2px; + *border-bottom-width: 2px; +} + +.dropdown-menu.bottom-up +{ + top: auto; + bottom: 100%; + margin-bottom: 2px; +} + +.dropdown-menu .divider +{ + height: 1px; + margin: 5px 1px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; + *width: 100%; + *margin: -5px 0 5px; +} + +.dropdown-menu a +{ + display: block; + padding: 3px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #555555; + white-space: nowrap; +} + +.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover +{ + color: #ffffff; + text-decoration: none; + background-color: #0088cc; +} + +.dropdown.open +{ + *z-index: 1000; +} + +.dropdown.open .dropdown-toggle +{ + color: #ffffff; + background: #ccc; + background: rgba(0, 0, 0, 0.3); +} + +.dropdown.open .dropdown-menu +{ + display: block; +} + +.typeahead +{ + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well +{ + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #eee; + border: 1px solid rgba(0, 0, 0, 0.05); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote +{ + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.fade +{ + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -ms-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; + opacity: 0; +} + +.fade.in +{ + opacity: 1; +} + +.collapse +{ + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -ms-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; + position: relative; + overflow: hidden; + height: 0; +} + +.collapse.in +{ + height: auto; +} + +.close +{ + float: right; + font-size: 20px; + font-weight: bold; + line-height: 18px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover +{ + color: #000000; + text-decoration: none !important; + opacity: 0.4; + filter: alpha(opacity=40); + cursor: pointer; +} + +.btn +{ + display: inline-block; + padding: 4px 10px 4px; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border: 1px solid #ccc; + border-bottom-color: #bbb; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + cursor: pointer; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + *margin-left: .3em; +} + +.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled] +{ + background-color: #e6e6e6; +} + +.btn:active,.btn.active +{ + background-color: #cccccc \9; +} + +.btn:first-child +{ + *margin-left: 0; +} + +.btn:hover +{ + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus +{ + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active,.btn:active +{ + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + outline: 0; +} + +.btn.disabled,.btn[disabled] +{ + cursor: default; + background-image: none; + background-color: #e6e6e6; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large +{ + padding: 9px 14px; + font-size: 15px; + line-height: normal; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.btn-large [class^="icon-"] +{ + margin-top: 1px; +} + +.btn-small +{ + padding: 5px 9px; + font-size: 11px; + line-height: 16px; +} + +.btn-small [class^="icon-"] +{ + margin-top: -1px; +} + +.btn-mini +{ + padding: 2px 6px; + font-size: 11px; + line-height: 14px; +} + +.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover +{ + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + color: #ffffff; +} + +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-dark.active +{ + color: rgba(255, 255, 255, 0.75); +} + +.btn-primary +{ + background-color: #393939; + background-image: -moz-linear-gradient(top, #454545, #262626); + background-image: -ms-linear-gradient(top, #454545, #262626); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#454545), to(#262626)); + background-image: -webkit-linear-gradient(top, #454545, #262626); + background-image: -o-linear-gradient(top, #454545, #262626); + background-image: linear-gradient(top, #454545, #262626); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#454545', endColorstr='#262626', GradientType=0); + border-color: #262626 #262626 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled] +{ + background-color: #262626; +} + +.btn-primary:active,.btn-primary.active +{ + background-color: #003399 \9; +} + +.btn-warning +{ + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -ms-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(top, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled] +{ + background-color: #f89406; +} + +.btn-warning:active,.btn-warning.active +{ + background-color: #c67605 \9; +} + +.btn-danger +{ + background-color: #da4f49; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(top, #ee5f5b, #bd362f); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0); + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled] +{ + background-color: #bd362f; +} + +.btn-danger:active,.btn-danger.active +{ + background-color: #942a25 \9; +} + +.btn-success +{ + background-color: #5bb75b; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -ms-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(top, #62c462, #51a351); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0); + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled] +{ + background-color: #51a351; +} + +.btn-success:active,.btn-success.active +{ + background-color: #408140 \9; +} + +.btn-info +{ + background-color: #49afcd; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(top, #5bc0de, #2f96b4); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0); + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled] +{ + background-color: #2f96b4; +} + +.btn-info:active,.btn-info.active +{ + background-color: #24748c \9; +} + +.btn-inverse +{ + background-color: #393939; + background-image: -moz-linear-gradient(top, #454545, #262626); + background-image: -ms-linear-gradient(top, #454545, #262626); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#454545), to(#262626)); + background-image: -webkit-linear-gradient(top, #454545, #262626); + background-image: -o-linear-gradient(top, #454545, #262626); + background-image: linear-gradient(top, #454545, #262626); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#454545', endColorstr='#262626', GradientType=0); + border-color: #262626 #262626 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} + +.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled] +{ + background-color: #262626; +} + +.btn-inverse:active,.btn-inverse.active +{ + background-color: #0c0c0c \9; +} + +button.btn,input[type="submit"].btn +{ + *padding-top: 2px; + *padding-bottom: 2px; +} + +button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner +{ + padding: 0; + border: 0; +} + +button.btn.large,input[type="submit"].btn.large +{ + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.small,input[type="submit"].btn.small +{ + *padding-top: 3px; + *padding-bottom: 3px; +} + +.btn-group +{ + position: relative; + *zoom: 1; + *margin-left: .3em; +} + +.btn-group:before,.btn-group:after +{ + display: table; + content: ""; +} + +.btn-group:after +{ + clear: both; +} + +.btn-group:first-child +{ + *margin-left: 0; +} + +.btn-group+.btn-group +{ + margin-left: 5px; +} + +.btn-toolbar +{ + margin-top: 9px; + margin-bottom: 9px; +} + +.btn-toolbar .btn-group +{ + display: inline-block; + *display: inline; + *zoom: 1; +} + +.btn-group .btn +{ + position: relative; + float: left; + margin-left: -1px; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group .btn:first-child +{ + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} + +.btn-group .btn:last-child,.btn-group .dropdown-toggle +{ + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} + +.btn-group .btn.large:first-child +{ + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} + +.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle +{ + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} + +.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active +{ + z-index: 2; +} + +.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle +{ + outline: 0; +} + +.btn-group .dropdown-toggle +{ + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + *padding-top: 5px; + *padding-bottom: 5px; +} + +.btn-group.open +{ + *z-index: 1000; +} + +.btn-group.open .dropdown-menu +{ + display: block; + margin-top: 1px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.btn-group.open .dropdown-toggle +{ + background-image: none; + -webkit-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn .caret +{ + margin-top: 7px; + margin-left: 0; +} + +.btn:hover .caret,.open.btn-group .caret +{ + opacity: 1; + filter: alpha(opacity=100); +} + +.btn-primary .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret +{ + border-top-color: #ffffff; + opacity: 0.75; + filter: alpha(opacity=75); +} + +.btn-small .caret +{ + margin-top: 4px; +} + +.alert +{ + padding: 8px 35px 8px 14px; + margin-bottom: 18px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert,.alert-heading +{ + color: #c40022; +} + +.alert .close +{ + position: relative; + top: -2px; + right: -25px; + line-height: 18px; +} + +.alert-success +{ + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success,.alert-success .alert-heading +{ + color: #468847; +} + +.alert-danger,.alert-error +{ + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading +{ + color: #c40022; +} + +.alert-info +{ + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info,.alert-info .alert-heading +{ + color: #3a87ad; +} + +.alert-block +{ + padding-top: 10px; + padding-bottom: 10px; +} + +.alert-block>p,.alert-block>ul +{ + margin-bottom: 0; +} + +.alert-block p+p +{ + margin-top: 5px; +} + +.nav +{ + margin-left: 0; + margin-bottom: 18px; + list-style: none; +} + +.nav>li>a +{ + display: block; +} + +.nav>li>a:hover +{ + text-decoration: none; + background-color: #eeeeee; +} + +.nav .nav-header +{ + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 18px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li+.nav-header +{ + margin-top: 9px; +} + +.nav-list +{ + padding-left: 14px; + padding-right: 14px; + margin-bottom: 0; +} + +.nav-list>li>a,.nav-list .nav-header +{ + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list>li>a +{ + padding: 3px 15px; +} + +.nav-list .active>a,.nav-list .active>a:hover +{ + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"] +{ + margin-right: 2px; +} + +.nav-tabs,.nav-pills +{ + *zoom: 1; +} + +.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after +{ + display: table; + content: ""; +} + +.nav-tabs:after,.nav-pills:after +{ + clear: both; +} + +.nav-tabs>li,.nav-pills>li +{ + float: left; +} + +.nav-tabs>li>a,.nav-pills>li>a +{ + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs +{ + border-bottom: 1px solid #ddd; +} + +.nav-tabs>li +{ + margin-bottom: -1px; +} + +.nav-tabs>li>a +{ + padding-top: 9px; + padding-bottom: 9px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs>li>a:hover +{ + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs>.active>a,.nav-tabs>.active>a:hover +{ + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} + +.nav-pills>li>a +{ + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills .active>a,.nav-pills .active>a:hover +{ + color: #ffffff; + background-color: #222; +} + +.nav-stacked>li +{ + float: none; +} + +.nav-stacked>li>a +{ + margin-right: 0; +} + +.nav-tabs.nav-stacked +{ + border-bottom: 0; +} + +.nav-tabs.nav-stacked>li>a +{ + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked>li:first-child>a +{ + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs.nav-stacked>li:last-child>a +{ + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.nav-tabs.nav-stacked>li>a:hover +{ + border-color: #ddd; + z-index: 2; +} + +.nav-pills.nav-stacked>li>a +{ + margin-bottom: 3px; +} + +.nav-pills.nav-stacked>li:last-child>a +{ + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu +{ + margin-top: 1px; + border-width: 1px; +} + +.nav-pills .dropdown-menu +{ + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret +{ + border-top-color: #0088cc; + margin-top: 6px; +} + +.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret +{ + border-top-color: #005580; +} + +.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret +{ + border-top-color: #333333; +} + +.nav>.dropdown.active>a:hover +{ + color: #000000; + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover +{ + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret +{ + border-top-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open>a:hover +{ + border-color: #999999; +} + +.tabbable +{ + *zoom: 1; +} + +.tabbable:before,.tabbable:after +{ + display: table; + content: ""; +} + +.tabbable:after +{ + clear: both; +} + +.tab-content +{ + overflow: hidden; +} + +.tab-content.dropdown_fix +{ + overflow: visible; +} + +.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs +{ + border-bottom: 0; +} + +.tab-content>.tab-pane,.pill-content>.pill-pane +{ + display: none; +} + +.tab-content>.active,.pill-content>.active +{ + display: block; +} + +.tabs-below .nav-tabs +{ + border-top: 1px solid #ddd; +} + +.tabs-below .nav-tabs>li +{ + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below .nav-tabs>li>a +{ + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below .nav-tabs>li>a:hover +{ + border-bottom-color: transparent; + border-top-color: #ddd; +} + +.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover +{ + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li +{ + float: none; +} + +.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a +{ + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left .nav-tabs +{ + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left .nav-tabs>li>a +{ + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left .nav-tabs>li>a:hover +{ + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover +{ + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right .nav-tabs +{ + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right .nav-tabs>li>a +{ + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right .nav-tabs>li>a:hover +{ + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover +{ + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.navbar +{ + overflow: visible; + margin-bottom: 18px; +} + +.navbar-inner +{ + padding-left: 20px; + padding-right: 20px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} + +.btn-navbar +{ + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075); +} + +.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled] +{ + background-color: #222222; +} + +.btn-navbar:active,.btn-navbar.active +{ + background-color: #080808 \9; +} + +.btn-navbar .icon-bar +{ + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar+.icon-bar +{ + margin-top: 3px; +} + +.nav-collapse.collapse +{ + height: auto; +} + +.navbar .brand:hover +{ + text-decoration: none; +} + +.navbar .brand +{ + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + line-height: 1; + color: #ffffff; +} + +.navbar .navbar-text +{ + margin-bottom: 0; + line-height: 40px; + color: #999999; +} + +.navbar .navbar-text a:hover +{ + color: #ffffff; + background-color: transparent; +} + +.navbar .btn,.navbar .btn-group +{ + margin-top: 5px; +} + +.navbar .btn-group .btn +{ + margin-top: 0; +} + +.navbar-form +{ + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before,.navbar-form:after +{ + display: table; + content: ""; +} + +.navbar-form:after +{ + clear: both; +} + +.navbar-form input,.navbar-form select +{ + display: inline-block; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-form .radio,.navbar-form .checkbox +{ + margin-top: 5px; +} + +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"] +{ + margin-top: 3px; +} + +.navbar-form .input-append,.navbar-form .input-prepend +{ + margin-top: 6px; + white-space: nowrap; +} + +.navbar-form .input-append input,.navbar-form .input-prepend input +{ + margin-top: 0; +} + +.navbar-search +{ + position: relative; + float: left; + margin-top: 6px; + margin-bottom: 0; +} + +.navbar-search .search-query +{ + padding: 4px 9px; + font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + color: #ffffff; + color: rgba(255, 255, 255, 0.75); + background: #666; + background: rgba(255, 255, 255, 0.3); + border: 1px solid #111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -ms-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-search .search-query :-moz-placeholder +{ + color: #eeeeee; +} + +.navbar-search .search-query::-webkit-input-placeholder +{ + color: #eeeeee; +} + +.navbar-search .search-query:hover +{ + color: #ffffff; + background-color: #999999; + background-color: rgba(255, 255, 255, 0.5); +} + +.navbar-search .search-query:focus,.navbar-search .search-query.focused +{ + padding: 5px 10px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} + +.navbar-fixed-top +{ + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.navbar-fixed-top .navbar-inner +{ + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar .nav +{ + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right +{ + float: right; +} + +.navbar .nav>li +{ + display: block; + float: left; +} + +.navbar .nav>li>a +{ + float: none; + padding: 10px 10px 11px; + line-height: 19px; + color: #999999; + text-decoration: none; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar .nav>li>a:hover +{ + background-color: transparent; + color: #ffffff; + text-decoration: none; +} + +.navbar .nav .active>a,.navbar .nav .active>a:hover +{ + color: #ffffff; + text-decoration: none; + background-color: #222222; +} + +.navbar .divider-vertical +{ + height: 40px; + width: 1px; + margin: 0 9px; + overflow: hidden; + background-color: #222222; + border-right: 1px solid #333333; +} + +.navbar .nav.pull-right +{ + margin-left: 10px; + margin-right: 0; +} + +.navbar .dropdown-menu +{ + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.navbar .dropdown-menu:before +{ + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} + +.navbar .dropdown-menu:after +{ + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} + +.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret +{ + border-top-color: #ffffff; +} + +.navbar .nav .active .caret +{ + opacity: 1; + filter: alpha(opacity=100); +} + +.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle +{ + background-color: transparent; +} + +.navbar .nav .active>.dropdown-toggle:hover +{ + color: #ffffff; +} + +.navbar .nav.pull-right .dropdown-menu +{ + left: auto; + right: 0; +} + +.navbar .nav.pull-right .dropdown-menu:before +{ + left: auto; + right: 12px; +} + +.navbar .nav.pull-right .dropdown-menu:after +{ + left: auto; + right: 13px; +} + +.breadcrumb +{ + padding: 7px 14px; + margin: 0 0 18px; + background-color: #fbfbfb; + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.breadcrumb li +{ + display: inline-block; + text-shadow: 0 1px 0 #ffffff; +} + +.breadcrumb .divider +{ + padding: 0 5px; + color: #999999; +} + +.breadcrumb .active a +{ + color: #333333; +} + +.pagination +{ + height: 36px; + margin: 18px 0; +} + +.pagination ul +{ + display: inline-block; + *display: inline; + *zoom: 1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination li +{ + display: inline; +} + +.pagination a +{ + float: left; + padding: 0 14px; + line-height: 34px; + text-decoration: none; + border: 1px solid #ddd; + border-left-width: 0; +} + +.pagination a:hover,.pagination .active a +{ + background-color: #f5f5f5; +} + +.pagination .active a +{ + color: #999999; + cursor: default; +} + +.pagination .disabled a,.pagination .disabled a:hover +{ + color: #999999; + background-color: transparent; + cursor: default; +} + +.pagination li:first-child a +{ + border-left-width: 1px; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} + +.pagination li:last-child a +{ + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} + +.pagination-centered +{ + text-align: center; +} + +.pagination-right +{ + text-align: right; +} + +.pager +{ + margin-left: 0; + margin-bottom: 18px; + list-style: none; + text-align: center; + *zoom: 1; +} + +.pager:before,.pager:after +{ + display: table; + content: ""; +} + +.pager:after +{ + clear: both; +} + +.pager li +{ + display: inline; +} + +.pager a +{ + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager a:hover +{ + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next a +{ + float: right; +} + +.pager .previous a +{ + float: left; +} + +.modal-open .dropdown-menu +{ + z-index: 2050; +} + +.modal-open .dropdown.open +{ + *z-index: 2050; +} + +.modal-open .popover +{ + z-index: 2060; +} + +.modal-open .tooltip +{ + z-index: 2070; +} + +.modal-backdrop +{ + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} + +.modal-backdrop.fade +{ + opacity: 0; +} + +.modal-backdrop,.modal-backdrop.fade.in +{ + opacity: 0.3; + filter: alpha(opacity=30); +} + +.modal +{ + position: fixed; + top: 50%; + left: 50%; + z-index: 1050; + max-height: 500px; + overflow: auto; + width: 560px; + margin: -250px 0 0 -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.loading +{ + height: 200px; + overflow: hidden; +} + +.modal.loading p +{ + font-size: 18px; + font-weight: bold; + text-align: center; + margin: 20px; + color: #999; +} + +.modal.fade +{ + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -ms-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} + +.modal.fade.in +{ + top: 50%; +} + +.modal-header +{ + padding: 10px 20px; +} + +.modal-header .close +{ + margin-top: 2px; +} + +.modal-body +{ + padding: 10px 20px; + max-height: 300px; + overflow-y:auto; + border-top: 1px #ccc solid; +} + +.modal-body .modal-form +{ + margin-bottom: 0; +} + +.modal-footer +{ + margin-bottom: 0; + background-color: #fff; + *zoom: 1; + padding: 10px 20px; + border-top: 1px #ccc solid; +} + +.modal-footer:before,.modal-footer:after +{ + display: table; + content: ""; +} + +.modal-footer:after +{ + clear: both; +} + +.modal-footer .btn +{ + float: left; + margin-right: 5px; + margin-bottom: 0; +} + +.tooltip +{ + position: absolute; + z-index: 1020; + display: block; + visibility: visible; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); +} + +.tooltip.in +{ + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top +{ + margin-top: -2px; +} + +.tooltip.right +{ + margin-left: 2px; +} + +.tooltip.bottom +{ + margin-top: 2px; +} + +.tooltip.left +{ + margin-left: -2px; +} + +.tooltip.top .tooltip-arrow +{ + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} + +.tooltip.left .tooltip-arrow +{ + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} + +.tooltip.bottom .tooltip-arrow +{ + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} + +.tooltip.right .tooltip-arrow +{ + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} + +.tooltip-inner +{ + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow +{ + position: absolute; + width: 0; + height: 0; +} + +.popover +{ + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + padding: 5px; +} + +.popover.top +{ + margin-top: -5px; +} + +.popover.right +{ + margin-left: 5px; +} + +.popover.bottom +{ + margin-top: 5px; +} + +.popover.left +{ + margin-left: -5px; +} + +.popover.top .arrow +{ + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} + +.popover.right .arrow +{ + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} + +.popover.bottom .arrow +{ + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} + +.popover.left .arrow +{ + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} + +.popover .arrow +{ + position: absolute; + width: 0; + height: 0; +} + +.popover-inner +{ + padding: 3px; + width: 280px; + overflow: hidden; + background: #000000; + background: rgba(0, 0, 0, 0.8); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); +} + +.popover-title +{ + padding: 9px 15px; + line-height: 1; + background-color: #f5f5f5; + border-bottom: 1px solid #eee; + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} + +.popover-content +{ + padding: 14px; + background-color: #ffffff; + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.popover-content p,.popover-content ul,.popover-content ol +{ + margin-bottom: 0; +} + +.thumbnails +{ + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before,.thumbnails:after +{ + display: table; + content: ""; +} + +.thumbnails:after +{ + clear: both; +} + +.thumbnails>li +{ + float: left; + margin: 0 0 18px 20px; +} + +.thumbnail +{ + display: block; + padding: 4px; + line-height: 1; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); +} + +a.thumbnail:hover +{ + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail>img +{ + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} + +.thumbnail .caption +{ + padding: 9px; +} + +.label +{ + padding: 2px 4px 3px; + font-size: 11.049999999999999px; + font-weight: bold; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.label:hover +{ + color: #ffffff; + text-decoration: none; +} + +.label-important +{ + background-color: #b94a48; +} + +.label-important:hover +{ + background-color: #953b39; +} + +.label-warning +{ + background-color: #f89406; +} + +.label-warning:hover +{ + background-color: #c67605; +} + +.label-success +{ + background-color: #468847; +} + +.label-success:hover +{ + background-color: #356635; +} + +.label-info +{ + background-color: #3a87ad; +} + +.label-info:hover +{ + background-color: #2d6987; +} + +@-webkit-keyframes progress-bar-stripes +{ + from{background-position: 0 0; +} + +to +{ + background-position: 40px 0; +} + +}@-moz-keyframes progress-bar-stripes +{ + from{background-position: 0 0; +} + +to +{ + background-position: 40px 0; +} + +}@keyframes progress-bar-stripes +{ + from{background-position: 0 0; +} + +to +{ + background-position: 40px 0; +} + +}.progress +{ + overflow: hidden; + height: 18px; + margin-bottom: 18px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(top, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.progress .bar +{ + width: 0%; + height: 18px; + color: #ffffff; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -ms-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(top, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -ms-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress-striped .bar +{ + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar +{ + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar +{ + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar +{ + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar +{ + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -ms-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(top, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); +} + +.progress-success.progress-striped .bar +{ + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar +{ + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(top, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar +{ + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion +{ + margin-bottom: 18px; +} + +.accordion-group +{ + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading +{ + border-bottom: 0; +} + +.accordion-heading .accordion-toggle +{ + display: block; + padding: 8px 15px; +} + +.accordion-inner +{ + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel +{ + position: relative; + margin-bottom: 18px; + line-height: 1; +} + +.carousel-inner +{ + overflow: hidden; + width: 100%; + position: relative; +} + +.carousel .item +{ + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -ms-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel .item>img +{ + display: block; + line-height: 1; +} + +.carousel .active,.carousel .next,.carousel .prev +{ + display: block; +} + +.carousel .active +{ + left: 0; +} + +.carousel .next,.carousel .prev +{ + position: absolute; + top: 0; + width: 100%; +} + +.carousel .next +{ + left: 100%; +} + +.carousel .prev +{ + left: -100%; +} + +.carousel .next.left,.carousel .prev.right +{ + left: 0; +} + +.carousel .active.left +{ + left: -100%; +} + +.carousel .active.right +{ + left: 100%; +} + +.carousel-control +{ + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right +{ + left: auto; + right: 15px; +} + +.carousel-control:hover +{ + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-caption +{ + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 10px 15px 5px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4,.carousel-caption p +{ + color: #ffffff; +} + +.hero-unit +{ + padding: 60px; + margin-bottom: 30px; + background-color: #f5f5f5; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 +{ + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; +} + +.hero-unit p +{ + font-size: 18px; + font-weight: 200; + line-height: 27px; +} + +.pull-right +{ + float: right; +} + +.pull-left +{ + float: left; +} + +.hide +{ + display: none; +} + +.show +{ + display: block; +} + +.invisible +{ + visibility: hidden; +} + +/* new clearfix */ + +.clearfix:after +{ + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} + +* html .clearfix +{ + zoom: 1; +} + +/* IE6 */ + +*:first-child+html .clearfix +{ + zoom: 1; +} + + +a +{ + color: #1d71bf; +} + +ul +{ + list-style: none; + margin: 0; +} + +dt +{ + font-weight: bold; +} + +#main_content +{ + /*padding-left: 250px; + padding-right: 25px;*/ + width: 970px; + padding: 30px; + margin: 0 auto; + background-color: #fff; + border: 1px #ccc solid; + margin-top: 52px; +} + +.topbar +{ + +} + +.topbar .switcher_bar +{ + display: inline-block; + height: auto; + width: 160px; + background-position: 140px center; + margin-bottom: 0; + font-size: 11px; + padding: 0; +} + +.topbar .switcher_bar a +{ + padding: 2px 10px 1px; + margin-left: 0; + display: block; +} + +.topbar .switcher_bar ul +{ + width: 130px; +} + +#user_info +{ + color: #ccc; + margin: auto 0; + margin-top: -170px; +} + +#user_info > a +{ + margin-left: 25px; + font-size: 13px !important; + color: #ccc; +} + +.page-header +{ + margin: 0; + padding: 0; + border: 0; + font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; +} + +h2 +{ + color: #c40022; + font-size: 24px; + font-weight: bold; + margin-bottom: 10px; +} + +body +{ + background-color: #ddd; + min-width: 890px; +} + +/* Login Splash Page */ + +#splash +{ + +} + +#splash .login +{ + padding-left: 290px; + background: #fff url(../img/Rackspace_Cloud_Company.png) no-repeat 49px 135px; + width: 360px; + min-height: 364px; + position: absolute; + top: 50%; + left: 50%; + margin: -192px 0 0 -325px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.2); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +#splash .login .alert +{ + margin-left: 50px; + margin-right: 70px; + padding: 10px; +} + +#splash .login form +{ + border-left: 1px #ddd solid; + padding: 0; +} + +#splash .login .modal-body +{ + padding: 0; + border-top: none; +} + +#splash .login .modal-header +{ + margin-top: 28px; + border-bottom: none; + border-left: 1px #ddd solid; + padding-left: 49px; + padding-top: 45px; + padding-bottom: 20px; +} + +#splash .login .modal-header h3 +{ + font-size: 13px; +} + +#splash .alert-block +{ + margin-left: 48px; + margin-right: 49px; + padding-right: 20px; + padding-left: 6px; +} + +#splash .alert-block .close +{ + right: -10px; + top: 0; +} + +#splash .modal-footer +{ + background-color: #fff; + border-top: none; + padding-bottom: 45px; + margin-bottom: 28px; + padding-left: 45px; +} + +#splash .modal-footer button +{ + cursor: pointer; + margin: 0; + line-height: 20px; + font-size: 13px; + text-align: center; + padding: 1px 10px 2px; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-width: 1px; + border-style: solid; + text-shadow: 0 -1px 1px rgba(0,0,0,0.4); + background-color: #a60004; + border-color: #a60004; + color: #fff; + background: #d81436; + background: -moz-linear-gradient(top,#d81436 0,#a60004 100%); + background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#d81436),color-stop(100%,#a60004)); + background: -webkit-linear-gradient(top,#d81436 0,#a60004 100%); + background: -o-linear-gradient(top,#d81436 0,#a60004 100%); + background: -ms-linear-gradient(top,#d81436 0,#a60004 100%); + background: linear-gradient(top,#d81436 0,#a60004 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d81436',endColorstr='#a60004',GradientType=0); +} + +#splash .modal-footer button::-moz-focus-inner +{ + border: none; + padding: 0; +} + +#splash .modal-footer button[disabled] +{ + cursor: default; + color: #fff; + background-color: #e9a7b2; + border-color: #e9a7b2; + background-image: none; +} + +#splash .modal-footer button:not([disabled]):hover +{ + background: #d81436; + background: -moz-linear-gradient(top,#d81436 0,#c40022 100%); + background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#d81436),color-stop(100%,#c40022)); + background: -webkit-linear-gradient(top,#d81436 0,#c40022 100%); + background: -o-linear-gradient(top,#d81436 0,#c40022 100%); + background: -ms-linear-gradient(top,#d81436 0,#c40022 100%); + background: linear-gradient(top,#d81436 0,#c40022 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d81436',endColorstr='#c40022',GradientType=0); +} + +#splash .modal-footer button:not([disabled]):active +{ + background-color: #c40022; + background-image: none; + box-shadow: inset 0 0 4px 0 rgba(0,0,0,0.5); + -moz-box-shadow: inset 0 0 4px 0 rgba(0,0,0,0.5); + -webkit-box-shadow: inset 0 0 4px 0 rgba(0,0,0,0.5); +} + +#splash .modal-footer .pull-right +{ + float: none; +} + + +#splash .login form .control-group +{ + padding-left: 49px; +} + +#splash .login input +{ + width: 244px; +} + + +#splash .help-block +{ + display: none; +} + +#container +{ + background: url(../img/body_bkg.gif) repeat-x 0 0; +} + +.nav li a +{ + text-shadow: none; +} + +.container-fluid +{ + padding-left: 0; +} + +.sidebar +{ + width: 1030px; + margin: 0 auto; +} + +.sidebar h4 +{ +display: none; +} + +.sidebar .nav-tabs +{ + margin: 0; + float: left; + margin-top: 55px; + border-bottom: none; +} + +.sidebar .nav-tabs li +{ + margin-left: 20px; +} + +.sidebar .nav-tabs li a +{ + background-color: transparent; + border: none; + font-weight: bold; + color: #888; + font-size: 14px; + display: inline-block; + padding: 6px 10px; + text-decoration: none; + text-shadow: 0 1px #fff; + margin: 0; + outline: none; +} + +.sidebar .nav-tabs li.active a +{ + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border: 1px #ccc solid; + background-color: #fff; + color: #333; + margin-top: -1px; +} + + + + + +h1.brand +{ + margin: 0; + float: left; + margin-top: 20px; +} + +h1.brand:before { + content: ''; + display: block; + height: 20px; + margin-bottom: -20px; + background-color: #fff; + border-radius: 2px 2px 0 0; + -moz-border-radius: 2px 2px 0 0; + -webkit-border-radius: 2px 2px 0 0; +} + + +h1.brand a +{ + display: block; + float: left; + width: 150px; + height: 46px; + text-indent: -9999px; + position: ; + background: url(../img/Rackspace_Cloud_Company_Small.png) no-repeat center center; + padding: 24px 24px 8px 16px; +} + +/* Tenant Dropdown */ + +a.current_item +{ + width: 163px; + float: left; +} + +a.current_item:hover +{ + text-decoration: none; +} + +a.current_item:hover h3, a.current_item:hover h4 +{ + color: #39738c; +} + +.switcher_bar +{ + width: 226px; + height: 25px; + float: left; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + color: #333333; + text-align: left; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border: 1px solid #ccc; + border-bottom-color: #bbb; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05); + cursor: pointer; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + *margin-left: .3em; + margin-top: 2px; +} + +.switcher_bar:hover +{ + background-image: none; + background-color: #fefefe; + background-image: -moz-linear-gradient(top, #ffffff, #efefef); + background-image: -ms-linear-gradient(top, #ffffff, #efefef); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#efefef)); + background-image: -webkit-linear-gradient(top, #ffffff, #efefef); + background-image: -o-linear-gradient(top, #ffffff, #efefef); + background-image: linear-gradient(top, #ffffff, #efefef); + background-repeat: repeat-x; +} + +a.dropdown-toggle:hover h3 +{ + text-decoration: none; + color: #c41022; +} + +.switcher_bar:focus +{ + outline: none; +} + +.switcher_bar h3 +{ + color: #666; + font-size: 16px; + margin: 0; + padding: 0px 0 0 16px; + text-shadow: 0 1px #fff; + text-align: left; + width: 176px; + overflow: hidden; + border-right: 1px #ccc solid; +} + +.switcher_bar .dropdown-toggle, .switcher_bar .open .dropdown-toggle +{ + text-decoration: none; + background-image: url(../img/drop_arrow.png) !important; + background-color: transparent !important; + background-repeat: no-repeat !important; + background-position: 202px center !important; + width: 100%; + display: inline-block; +} + +.switcher_bar h4 +{ + color: #6fabc4; + font-size: 10px; + text-transform: uppercase; + font-weight: normal; + padding: 0; +} + +.switcher_bar ul +{ + border: 1px solid #ccc; + + width: 224px; + margin-top: 0; + padding-top: 0; + padding-bottom: 10px; +} + +.switcher_bar .dropdown-menu li a +{ + color: #1d71bf; + font-size: 16px !important; + line-height: 26px; + padding-top: 0; + padding-bottom: 0; +} + +.switcher_bar .dropdown-menu li a:hover +{ + text-decoration: underline !important; + background: transparent !important; + color: #1d71bf !important; +} + +#usage +{ + margin-bottom: 25px; + height: 125px; +} + +.usage_block +{ + background: #e8f8ff; + color: #84b6c5; + border: 1px solid #afe3fb; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + float: left; + width: 29%; + margin-right: 5%; + min-height: 125px; +} + +.usage_block.last +{ + margin-right: 0; +} + +.usage_block h3 +{ + background: #cef0ff; + color: #4fa5bf; + font-weight: normal; + padding: 0 0 0 10px; + border-bottom: 1px solid #c6e7f5; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.usage_block ul +{ + margin: 10px; +} + +.usage_block .quantity +{ + font-size: 25px; +} + +.usage_block li +{ + font-size: 11px; + margin: 0 0 15px 0; +} + +.usage_block .unit +{ + font-size: 11px; + text-transform: uppercase; + padding: 0 0 0 1px; +} + +.table-bordered +{ + border: none; +} + +.table_header +{ + min-height: 35px; + padding: 0 !important; +} + +.table_caption th +{ + background-color: transparent; + border: none; +} + +.table-bordered tr.table_caption + tr th +{ + border-top: 1px solid #ddd; +} + +.table-bordered tr.table_caption + tr th:first-child, +.table-bordered tr.table_caption + tr th.hide + th +{ + border-left: 1px solid #ddd; +} + +.table-bordered tr.table_caption + tr th:last-child +{ + border-right: 1px solid #ddd; +} + +.table-bordered tbody tr td:first-child, +.table-bordered tfoot tr td:first-child +{ + border-left: 1px solid #ddd !important; +} + +.table-bordered tbody tr td:last-child, +.table-bordered tfoot tr td:last-child +{ + border-right: 1px solid #ddd; +} + +.table-bordered tfoot tr td:first-child +{ + border-bottom: 1px solid #ddd; +} + +.table-bordered tfoot tr td:last-child +{ + border-bottom: 1px solid #ddd; +} + +.table_title h3, .table_header h3 +{ + color: #333; + font-size: 18px; + font-weight: bold; + margin-bottom: 5px; + float: left; +} + +th +{ + background: #f1f1f1; +} + +small +{ + font-size: 11px; +} + +.main_nav +{ + list-style: none; + float: left; + margin: 0; + padding-left: 10px; + height: 30px; +} + +.main_nav a +{ + color: #888; + font-size: 13px; + font-weight: bold; + float: left; + padding: 7px 20px; + outline: none; + text-shadow: 0 1px #fff; +} + +.main_nav a:hover +{ + text-decoration: none; + color: #c41022; +} + +.main_nav li +{ + float: left; +} + +.main_nav a.active +{ + background: url(../img/selected_arrow.png) no-repeat center bottom; +} + +table form +{ + margin-bottom: 0; + width: 1px; +} + +.alert-block .alert-actions +{ + margin-top: -23px; + margin-right: -23px; +} + +.modal > form, +.login > form, +.alert-actions > form +{ + margin-bottom: 0; +} + +.alert-block p +{ + overflow: hidden; + word-wrap: break-word; +} + +.alert-block p:last-child +{ + margin-bottom: 0; +} + +#actions.single +{ + width: 90px; +} + +.table-striped tr td +{ + transition: background 0.2s; + -webkit-transition: background 0.2s; + -moz-transition: background 0.2s; + -o-transition: background 0.2s; +} + +.inspect +{ + float: left; + display: block; + margin-top: 5px; + margin-right: 25px; +} + +.table +{ + margin-bottom: 0px; + margin-top: 30px; +} + +.table tr td +{ + vertical-align: middle; +} + +.table tr.empty td +{ + text-align: center; +} + +.table tfoot tr td +{ + border-top: 1px solid #DDD; + font-size: 13px; + line-height: 20px; + padding: 2px 10px; + color: #aaa; +} + +.table_actions +{ + float: right; + min-width: 400px; + margin-bottom: 10px; +} + +.table_actions .table_search +{ + display: inline-block; +} + +.table_search input +{ + background: url(../img/search.png) no-repeat 190px 5px; + display: inline-block; + margin-bottom: 0; +} + +.table_actions a, .table_actions button +{ + float: right; + margin-left: 10px; +} + +.table_actions button.filter +{ + margin-left: 0; +} + +.table_header .table_actions +{ + min-width: 0; +} + +.table_header .table_actions a, .table_header .table_actions button +{ + display: inline-block; + float: none; +} + +.table_actions form +{ + float: right; + margin-left: 10px; +} + +.hidden +{ + display: none; +} + +.table-striped tbody tr.status_unknown:nth-child(odd) td +{ + background-color: #ffffb5; +} + +.table-striped tbody tr.status_unknown:nth-child(even) td +{ + background-color: #ffffc6; +} + +tbody .nowrap-col +{ + white-space: nowrap; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + cursor: default; +} + +tbody .nowrap-col:hover +{ + overflow: visible; + max-width: none; + background-color: #eee !important; +} + +.icon-updating.ajax-updating +{ + background: transparent url(../img/spinner.gif) no-repeat center center; + padding: 1px; +} + +td .icon-updating.ajax-updating +{ + margin-right: 5px; +} + +.overview +{ + font-size: 24px; +} + +#monitoring +{ + background: #f8f8f8; + font-size: 14px; + height: 20px; + margin: -18px 0 25px; + padding: 10px; + border: 1px solid #e1e1e1; + font-family: "anivers"; +} + +#monitoring h3 +{ + font-size: 14px; + font-weight: normal; + float: left; + line-height: 18px; +} + +#external_links, #external_links li +{ + float: left; +} + +#external_links li +{ + margin: 0 0 0 15px; +} + +/* Forms */ + +form label +{ + text-align: left; + color: #333; +} + +.modal +{ + max-height: none; + /* Prevents large modals from scrolling unnecessarily */ + top: 80px; + margin-top: 0; + position: absolute; + width: auto; + width: 560px +} + + +form.horizontal .form-field +{ + float: left; +} + +form.horizontal.split_half .form-field +{ + width: 334px; +/* + +Fits 2 fields to a row */ +} + +form.horizontal.split_quarter .form-field +{ + width: 167px; +/* + +Fits 4 fields to a row */ +} + +form.horizontal.split_five .form-field +{ + width: 133px; +/* + +Fits 5 fields to a row */ +} + +form.horizontal fieldset +{ + width: 100%; +} + +.modal-body table td +{ + vertical-align: top; +} + +.modal-body ~ hr +{ + margin-bottom: 0; +} + +.static_page +{ + width: 700px; + background-color: #FFF; + border: 1px solid #DDD; +} + +.static_page > form +{ + margin-bottom: 0; +} + +.left +{ + float: left; + width: 347px; + margin-right: 15px; +} + +.left form +{ + margin: 0; +} + +.right +{ + float: left; + width: 308px; +} + +.clear +{ + clear: both; + width: 0; + height: 0; + padding: 0; + margin: 0; +} + +.modal-body fieldset +{ + margin: 0; + padding: 0; +} + +.modal-body fieldset ul +{ + width: 90%; +} + +.modal-body .left, .modal-body .right +{ + float: left; + width: 48%; + margin: 0; + margin-right: 1%; +} + + + +.modal-body fieldset .form-field input, +.modal-body fieldset .form-field select, +.modal-body fieldset .form-field textarea +{ + width: 90%; +} + +.modal-footer input +{ + width: auto; +} + +.modal-body .modal-footer +{ + width: 670px; + margin-left: -25px; + margin-right: -15px; +} + +.modal-footer a.close +{ + margin-top: 0; + margin-right: 5px; + font-size: 12px; + color: #666; + font-weight: normal; + filter: alpha(opacity=100); + -khtml-opacity: 1; + -moz-opacity: 1; + opacity: 1; + float: left; +} + +.modal-footer .pull-right +{ + float: left; +} + +.modal-footer a.close:hover +{ + color: #333; + text-decoration: underline; +} + +.modal-body .help-block +{ + text-align: left; + float: left; + width: 100%; + margin-bottom: 10px; +} + +#create_keypair_modal .clearfix +{ + margin-bottom: 115px; +} + +#actions +{ + width: 90px; +} + +#actions .btn +{ + margin-bottom: 5px; +} + +#actions a.btn +{ + width: 70px; +} + +#actions input.btn +{ + text-align: left; +} + +#images #actions +{ + width: 100px; +} + +/*New List Patches*/ + +.details-modal .modal-body +{ + padding-bottom: 20px; +} + +.form-inline +{ + display: inline; +} + +td.select +{ + width: 10px; +} + +/* Actions dropdown */ + +td.actions_column +{ + padding: 10px; + position: relative; + min-width: 115px; + min-height: 20px; +} + +td.actions_column .row_actions a, +td.actions_column .row_actions input, +td.actions_column .row_actions button +{ + background: none; + float: none; + display: block; + padding: 5px 10px; + color: #1d71bf; + text-align: left; + border-radius: 0; + border: 0 none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + font-size: 13px; +} + +td.actions_column .row_actions .hide +{ + display: none; +} + +/* Makes size consistent across browsers when mixing "btn-group" and "small" */ + +.btn.hide, .btn-group .hide +{ + display: none; +} + +.btn-group .dropdown-toggle:focus +{ + outline: none; +} + +.dropdown-menu button +{ + line-height: 18px; + + +/* Matches rule for ".dropdown-menu a" in bootstrap */ + width: 100%; +} + +.btn-group .dropdown-menu .btn +{ + border-radius: 0; +} + +.dropdown-menu .btn.btn-danger, +.dropdown-menu .btn.btn-danger:hover, +.dropdown-menu .btn.btn-success, +.dropdown-menu .btn.btn-success:hover, +.dropdown-menu .btn.btn-info, +.dropdown-menu .btn.btn-info:hover +{ + text-shadow: none; +/ + +* remove default bootstrap shadowing from button text. */ +} + +.dropdown-menu li.divider +{ + margin-bottom: 10px; + margin-top: 0; +} + +.dropdown-menu li:hover +{ + background: none; +} + +.dropdown-menu li.divider:hover +{ + background-color: #E5E5E5; +} + +td.actions_column .dropdown-menu a:hover, +td.actions_column .dropdown-menu button:hover +{ + text-decoration: underline; +} + +.dropdown-menu .btn.btn-danger +{ + color: #C43C35; +} + +.dropdown-menu .btn.btn-danger:hover +{ +} + +/* Overrides for single-action rows (no dropdown) */ + +tr td.actions_column ul.row_actions.single, +tr:hover td.actions_column ul.row_actions.single, +td.actions_column ul.row_actions.single, +td.actions_column ul.row_actions.single:hover +{ + border: none; +} + +td.actions_column ul.row_actions.single li.action +{ + display: block; +} + +td.actions_column ul.row_actions.single li.action:hover +{ + background-color: transparent; +} + +td.actions_column ul.row_actions.single a, +td.actions_column ul.row_actions.single input, +td.actions_column ul.row_actions.single button +{ + color: #43a1d6; +} + +td.actions_column ul.row_actions.single a:hover, +td.actions_column ul.row_actions.single input:hover, +td.actions_column ul.row_actions.single button:hover +{ + color: black; +} + +th.multi_select_column, td.multi_select_column +{ + +} + +th.multi_select_column, td.multi_select_column +{ + text-align: center; +} + +.table input[type="checkbox"] +{ + display: inline; +} + +div.input input[type="checkbox"] +{ + float: left; + width: 25px; +} + +.table_title a +{ + font-size: 11px; + float: right; + margin-left: 10px; + margin-top: 10px; +} + +tr.terminated +{ + color: #999999; +} + +#instance_tabs +{ + float: left; + width: 100%; + border-bottom: 1px solid #e1e1e1; +} + +#instance_tabs li a +{ + background: #f2f2f2; + display: block; + font-size: 14px; + float: left; + padding: 5px 10px; + margin-right: 10px; + border: 1px solid #e1e1e1; + border-bottom: none; +} + +#instance_tabs li.active a +{ + background: #fff; + padding-bottom: 8px; + margin-bottom: -5px; +} + +#main_content .nav-tabs +{ + margin-bottom: 0; +} + +#main_content .tab-content +{ + border: 1px solid #ddd; + border-top: 0 none; + padding: 30px; +} + +.tab_wrapper +{ + padding-top: 50px; +} + +/* Fix tooltip z-index to show above modals. Bootstrap bug 582*/ + +.tooltip +{ + z-index: 12000; +} + +.volume_boot_disclosure +{ + font-weight: bold; + color: #555; + cursor: pointer; + background-image: url(../img/right_droparrow.png); + background-repeat: no-repeat; + background-position: 130px center; +} + +.volume_boot_disclosure.on +{ + width: 334px; + margin-bottom: 10px; + border-bottom: solid 1px #E1E1E1; + background-image: url(../img/drop_arrow.png); +} + +#splash form div.clearfix.error +{ + width: 254px; +} + +/* Region selector in header */ + +#region_selector +{ + position: absolute; + z-index: 9999; + right: 0; + top: 24px; +} + +#region_selector a +{ + margin-left: 0; +} + +#region_selector ul +{ + float: left; + margin-left: 5px; + padding-right: 21px; + width: 125px; +} + +#region_selector ul:hover a +{ + display: block; +} + +#region_selector li a +{ + padding: 3px 3px 3px 5px; + display: none; + background: #E1E1E1; + margin-top: -10px; +} + +#region_selector li:first-child p +{ + background: #EDEDED url(../img/drop_arrow.png) no-repeat 106px 9px !important; + display: block; + border: 1px solid #e1e1e1; + padding: 5px; +} + +iframe +{ + border: none; +} + +.item_detail ul li label +{ + color: #000; + font-weight: bold; + display: block; + margin-top: 5px; +} + +.progress_bar +{ + width: 100%; + height: 15px; + border: 1px solid #ddd; + background-color: #efefef; +} + +.progress_bar_fill +{ + height: 100%; + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); +} + +.quota_title +{ + color: #999; +} + +.quota_title strong +{ + color: #000; +} + +.quota_title strong span +{ + font-weight: normal; +} + +.quota_title p +{ + float: right; +} + +.quota_bar +{ + height: 15px; + margin: -5px 0 20px; +} + +#main_content .row-fluid +{ + margin: 0px; + +} + + +#main_content .tab-content .row-fluid h4 +{ + border-top: 1px solid #ccc; + color: #333; + font-size: 18px; + font-weight: bold; + margin-right: -30px; + margin-left: -30px; + padding-top: 20px; + padding-left: 30px; + margin-top: 0px; +} + +#main_content h3 +{ + margin-bottom: 20px; +} + +#main_content .row-fluid:last-child +{ + margin-bottom: 0; +} + +#main_content dt +{ + position: absolute; + width: 150px; + text-align: right; + line-height: 30px; + color: #999; + font-weight: normal; +} + +#main_content dd +{ + margin-left: 170px; + line-height: 30px; + color: #333; +} + +#main_content dd li +{ + line-height: 30px; +} + +.header_rule +{ + display: none; +} + +.item_detail .detail_section +{ + margin-bottom: 25px; + float: left; + margin-right: 50px; +} + +.error .help-inline +{ + display: block; + background: url(../img/alert_red.png) no-repeat 0 center; + padding-left: 20px; +} + +label.log-length +{ + line-height: 28px; + margin-right: 10px; +} + +.split_five div.control-group input[type="text"], +.split_five div.control-group select +{ + width: 120px; +} + +.form-row +{ + +} + +#activity +{ + padding: 10px 10px 0; + border: 1px #e6e6e6 solid; + margin-top: 20px; +} + +#activity span +{ + display: block; + margin-bottom: 10px; +} + +#activity span strong +{ + float: left; + width: 200px; + text-align: right; + margin-right: 20px; + font-weight: normal; + color: #999; +} + +.fake_table +{ + border: 1px #ccc solid; + margin: 0; +} + +.fake_table ul +{ + margin: 0 !important; + padding: 0; + width: 100% !important; +} + +.fake_table.fake_table_header +{ + padding: 5px 10px; + background-color: #eee; + border-bottom: none; +} + +.fake_table ul li .user_name +{ + float: left; + padding: 7px; + width: 90px; + overflow: hidden; +} + +.fake_table ul .active +{ + float: right; + padding: 2px; +} + +.fake_table ul .active .btn +{ + padding: 4px 8px; +} + +.fake_table .dropdown-menu li +{ + color: #006ec5; + line-height: 20px; + padding: 2px 10px; + cursor: pointer; +} + +.fake_table .dropdown-menu li:hover +{ + text-decoration: underline; +} + +.fake_table .dark_stripe +{ + background-color: #EFEFEF; +} + +.project_membership .header .help_text +{ + margin: 10px 0px; +} + +.no_results +{ + padding: 5px !important; +} diff --git a/chef/cookbooks/openstack-dashboard/files/default/horizon.key b/chef/cookbooks/openstack-dashboard/files/default/horizon.key new file mode 100644 index 0000000..afd6e48 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/files/default/horizon.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyH7O7R1/awNE2 +yVhLYchI1WOCojq9/O1iW1mlkbko77GwhvvoCcOGj+F60DLEtbVM3nLJnJE10JNF +qsI8fO1H3wUmvuJgBtbIvYHENuW9PoNlvzpH7PLAg0pruJK5qxZ1t6xA+mYC3Ozd +BriAVfOgaax1fzoH7hxidGz078DapjZA08Ay2vBkQiYiXw7qxfPtOiURowPOyFaO +8oRAbbRu5CZco7NdrDV1/a/9F1kxWCZQCJRugX5/F5XxWbu5Onh5GDj8NioePH2c +t00TYLIp801MOkhG+sCevDG2CIv6zQo4PtcrqbOKfih/HEwnKMgsloOQyp/FBPcP +Y8nrLUbdAgMBAAECggEBAIlI087A6QkB2X3fWzTmTWaNSN/zR1EM8qz++S4aDVqW +ux3yO48Qj2lT1ZZBhw+DeROg01m+WVDfy9JDFOdn5b2iDydYscMPIlWfcbeYFCNy +XfV6mGG9Jy/nQOGbZULCsVHTWWjPVPZZ7hlAR81p8ZauO6RnhYN9KtVbJGr3qm0Z +qizveBZloATAl/sBwQLWZKpPUv/9YSIAbP6UxbO9Izpe5iGgZM2Kpc2n360jD70n +OUYPO8q7CIY11qyk2LHqIg1Gxsc0cGARaMTdvV+gVnHDPX/i+imfu2oAGeV+Bdc9 +nTeffQyJugmh9lP2T/xEvrmTBkIqWrH01Nz4h/SChsUCgYEA4s7yFV4pxSoNmVZJ +qq7fcXoekGaePoH5p11VwYfnb313ZlLqcs20Z9bCDQrWIWIinKfr7rYypn6mJt8C +SDxOnPp3fBt4S/7A/vO6KbsRIRbSMollpqRZALKBLnxfKK5h2i5T9V2E9AAPibV+ +OiaoD+k522ZHlwE9FbVoWImQmi8CgYEAyQypQhJeVQCtlcj/lT2SJZOKCmfnUE2/ +SYP2MVTsy0257Rtabx/RBk7tzWxAt4Wfw90SciA2TBKOFcMsc2Xgh7cVXfSjae1B +8ja38hxPUBwoSjgUv7zDlwN7FyU08blL7gJNLnd9EaDsv21bk2UzAQVIZSNbhfKj +gwNRqd3ACLMCgYBZfRGjQK32bytCbvtN7wWWfnqEuxiAzUO1t2vYmkjEgpqTsu9y +MJuXxPJ3tujZ7yB88vxyCU1ex+XCvIbn/XN/GIwAWc5THccEUSIIyRhvF9QDwJZs +87dyQPwbXVMmd/d/4Hub+jQ1GbkHuqZ9RzdUX89GM8rXV9PENvlexZhaXQKBgHuc +YzySWplcr11WlX3Gu2KLEeZP9/JxutiSX/34vxZIlbXoiXOlVjjQsnkIw8mSSKZ9 +9+dfUsL27ZEHzP6udpw6vtJjpU1taIxphDNBoKoahdMMJAW3I3Jn23hhkenFCaAu +nhGhua3rCWSw8grvv6oy+03jrVPv2QSmN8F+66DtAoGAIcssttVuxPAuK63lTjLg +k2ozCJIZzdR23k/wUOpoXT8TXi9CXJGL5ddYb30mIeCMwR/A8zCopBBFqO8PmJtG +KMmKJmYVN2wuumlCziuM+G41fHdyYhnngRGco2F2BA7aRI3GEsDXbi8gwrcyaF88 +HRYlp01a5r41I/4FOxQ0djA= +-----END PRIVATE KEY----- diff --git a/chef/cookbooks/openstack-dashboard/files/default/horizon.pem b/chef/cookbooks/openstack-dashboard/files/default/horizon.pem new file mode 100644 index 0000000..bee3ddc --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/files/default/horizon.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICvjCCAaYCCQDz0B/8RKp2+TANBgkqhkiG9w0BAQUFADAhMR8wHQYDVQQDExZj +b250cm9sbGVyLmV4YW1wbGUuY29tMB4XDTEyMDQxMDE5NTU1NVoXDTIyMDQwODE5 +NTU1NVowITEfMB0GA1UEAxMWY29udHJvbGxlci5leGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALIfs7tHX9rA0TbJWEthyEjVY4KiOr38 +7WJbWaWRuSjvsbCG++gJw4aP4XrQMsS1tUzecsmckTXQk0Wqwjx87UffBSa+4mAG +1si9gcQ25b0+g2W/Okfs8sCDSmu4krmrFnW3rED6ZgLc7N0GuIBV86BprHV/Ogfu +HGJ0bPTvwNqmNkDTwDLa8GRCJiJfDurF8+06JRGjA87IVo7yhEBttG7kJlyjs12s +NXX9r/0XWTFYJlAIlG6Bfn8XlfFZu7k6eHkYOPw2Kh48fZy3TRNgsinzTUw6SEb6 +wJ68MbYIi/rNCjg+1yups4p+KH8cTCcoyCyWg5DKn8UE9w9jyestRt0CAwEAATAN +BgkqhkiG9w0BAQUFAAOCAQEAZuc+mrUyuhfmwkj8CzJSOoJ9NznHblE4z3wZBzqj +lbGX7cjUubAnCobuiJkiizJP1uqt5pCRKU7saGu31dYwZ3WR6xp+iAwss9feYeu0 +6atNz/e8RLHal+uP6FQaKL7QuiQx1O+g3ntKT+iWY3wYa1h3JuFV/VxOjGcwTJDX +lhpcz41B4L5C03V9VfJbk8Np/oRaNMT3DsUOwofafKWPnK7/snGKHd/I93rxk5z4 +xrvrkH0mohDkAOsUyd4Jgl3n7vQOeVq4uqkXdY4jlgRWykrGfn3JFkdol9VAU0ld +eHRZWBbIl9zOTZqHqtOCzHghhNAb+dy6+hrxta11ZqT/nA== +-----END CERTIFICATE----- diff --git a/chef/cookbooks/openstack-dashboard/metadata.rb b/chef/cookbooks/openstack-dashboard/metadata.rb new file mode 100644 index 0000000..2965ea1 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/metadata.rb @@ -0,0 +1,16 @@ +name "openstack-dashboard" +maintainer "AT&T Services, Inc." +maintainer_email "cookbooks@lists.tfoundry.com" +license "Apache 2.0" +description "Installs/Configures the OpenStack Dasboard (Horizon)" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.0" + +recipe "openstack-dashboard::server", "Sets up the Horizon dashboard within an Apache `mod_wsgi` container." + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "apache2" +depends "openstack-common", "~> 0.4.0" diff --git a/chef/cookbooks/openstack-dashboard/recipes/default.rb b/chef/cookbooks/openstack-dashboard/recipes/default.rb new file mode 100644 index 0000000..2b3bb4a --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: openstack-dashboard +# Recipe:: default +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/openstack-dashboard/recipes/server.rb b/chef/cookbooks/openstack-dashboard/recipes/server.rb new file mode 100644 index 0000000..65af496 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/recipes/server.rb @@ -0,0 +1,203 @@ +# +# Cookbook Name:: openstack-dashboard +# Recipe:: server +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +# +# Workaround to install apache2 on a fedora machine with selinux set to enforcing +# TODO(breu): this should move to a subscription of the template from the apache2 recipe +# and it should simply be a restorecon on the configuration file(s) and not +# change the selinux mode +# +execute "set-selinux-permissive" do + command "/sbin/setenforce Permissive" + action :run + + only_if "[ ! -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*enforcing') -eq 1 ]" +end + +platform_options = node["openstack"]["dashboard"]["platform"] + +include_recipe "apache2" +include_recipe "apache2::mod_wsgi" +include_recipe "apache2::mod_rewrite" +include_recipe "apache2::mod_ssl" + +# +# Workaround to re-enable selinux after installing apache on a fedora machine that has +# selinux enabled and is currently permissive and the configuration set to enforcing. +# TODO(breu): get the other one working and this won't be necessary +# +execute "set-selinux-enforcing" do + command "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" + action :run + + only_if "[ -e /etc/httpd/conf/httpd.conf ] && [ -e /etc/redhat-release ] && [ $(/sbin/sestatus | grep -c '^Current mode:.*permissive') -eq 1 ] && [ $(/sbin/sestatus | grep -c '^Mode from config file:.*enforcing') -eq 1 ]" +end + +identity_admin_endpoint = endpoint "identity-admin" +auth_admin_uri = ::URI.decode identity_admin_endpoint.to_s +identity_endpoint = endpoint "identity-api" +auth_uri = ::URI.decode identity_endpoint.to_s + +db_pass = db_password "horizon" +db_info = db "dashboard" + +python_packages = platform_options["#{db_info['db_type']}_python_packages"] +(platform_options["horizon_packages"] + python_packages).each do |pkg| + package pkg do + action :upgrade + options platform_options["package_overrides"] + end +end + +if node["openstack"]["dashboard"]["session_backend"] == "memcached" + platform_options["memcache_python_packages"].each do |pkg| + package pkg + end +end + +memcached = memcached_servers + +template node["openstack"]["dashboard"]["local_settings_path"] do + source "local_settings.py.erb" + owner "root" + group "root" + mode 00644 + + variables( + :db_pass => db_pass, + :db_info => db_info, + :auth_uri => auth_uri, + :auth_admin_uri => auth_admin_uri, + :memcached_servers => memcached + ) + + notifies :restart, "service[apache2]" +end + +# FIXME: this shouldn't run every chef run +# dashboard should not need db, so comment the following code block. +if "False" == "True" +execute "openstack-dashboard syncdb" do + cwd "/usr/share/openstack-dashboard" + environment ({'PYTHONPATH' => '/etc/openstack-dashboard:/usr/share/openstack-dashboard:$PYTHONPATH'}) + command "python manage.py syncdb --noinput" + action :run + # not_if "/usr/bin/mysql -u root -e 'describe #{node["dash"]["db"]}.django_content_type'" +end +end + +cookbook_file "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/certs/#{node["openstack"]["dashboard"]["ssl"]["cert"]}" do + source "horizon.pem" + mode 00644 + owner "root" + group "root" + + notifies :run, "execute[restore-selinux-context]", :immediately +end + +case node["platform"] +when "ubuntu","debian" + grp = "ssl-cert" +else + grp = "root" +end + +cookbook_file "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/private/#{node["openstack"]["dashboard"]["ssl"]["key"]}" do + source "horizon.key" + mode 00640 + owner "root" + group grp # Don't know about fedora + + notifies :run, "execute[restore-selinux-context]", :immediately +end + +# stop apache bitching +directory "#{node["openstack"]["dashboard"]["dash_path"]}/.blackhole" do + owner "root" + action :create +end + +template node["openstack"]["dashboard"]["apache"]["sites-path"] do + source "dash-site.erb" + owner "root" + group "root" + mode 00644 + + variables( + :ssl_cert_file => "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/certs/#{node["openstack"]["dashboard"]["ssl"]["cert"]}", + :ssl_key_file => "#{node["openstack"]["dashboard"]["ssl"]["dir"]}/private/#{node["openstack"]["dashboard"]["ssl"]["key"]}" + ) + + notifies :run, "execute[restore-selinux-context]", :immediately +end + +file "#{node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" do + action :delete + backup false + + only_if { platform?("fedora", "redhat", "centos") } # :pragma-foodcritic: ~FC024 - won't fix this +end + +# ubuntu includes their own branding - we need to delete this until ubuntu makes this a +# configurable paramter +package "openstack-dashboard-ubuntu-theme" do + action :purge + + only_if { platform?("ubuntu")} +end + +# The `apache_site` provided by the apache2 cookbook +# is not an LWRP. Guards do not apply to definitions. +# http://tickets.opscode.com/browse/CHEF-778 +if platform?("debian","ubuntu") then + apache_site "000-default" do + enable false + end +elsif platform?("fedora") then + apache_site "default" do + enable false + + notifies :run, "execute[restore-selinux-context]", :immediately + end +end + +apache_site "openstack-dashboard" do + enable true + + notifies :run, "execute[restore-selinux-context]", :immediately + notifies :reload, "service[apache2]", :immediately +end + +execute "restore-selinux-context" do + command "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + action :nothing + + only_if { platform?("fedora") } +end + +# TODO(shep) +# Horizon has a forced dependency on there being a volume service endpoint in your keystone catalog +# https://answers.launchpad.net/horizon/+question/189551 diff --git a/chef/cookbooks/openstack-dashboard/spec/default_spec.rb b/chef/cookbooks/openstack-dashboard/spec/default_spec.rb new file mode 100644 index 0000000..d9376b5 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/default_spec.rb @@ -0,0 +1,4 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::default" do +end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb new file mode 100644 index 0000000..b299da9 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/server-fedora_spec.rb @@ -0,0 +1,56 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::server" do + before { dashboard_stubs } + + describe "fedora" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::FEDORA_OPTS + @chef_run.converge "openstack-dashboard::server" + end + + it "deletes openstack-dashboard.conf" do + opts = ::FEDORA_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, true) + chef_run.converge "openstack-dashboard::server" + file = "/etc/httpd/conf.d/openstack-dashboard.conf" + + expect(chef_run).to delete_file file + end + + it "doesn't remove the default ubuntu virtualhost" do + resource = @chef_run.find_resource( + "execute", + "a2dissite 000-default" + ) + + expect(resource).to be_nil + end + + it "removes default virtualhost" do + resource = @chef_run.find_resource( + "execute", + "a2dissite default" + ).to_hash + + expect(resource[:params]).to include( + :enable => false + ) + end + + it "notifies restore-selinux-context" do + pending "TODO: how to test this occured on apache_site 'default'" + end + + it "executes restore-selinux-context" do + opts = ::FEDORA_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, true) + chef_run.converge "openstack-dashboard::server" + cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + + expect(chef_run).to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb new file mode 100644 index 0000000..c018c0a --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/server-opensuse_spec.rb @@ -0,0 +1,65 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::server" do + before { dashboard_stubs } + + describe "opensuse" do + context "mysql backend" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( + {"db_type" => "mysql", "db_name" => "flying_dolphin"}) + + @chef_run.converge "openstack-dashboard::server" + end + + it "installs mysql packages when mysql backend is configured" do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( + {"db_type" => "mysql", "db_name" => "flying_dolphin"}) + @chef_run.converge "openstack-dashboard::server" + + expect(@chef_run).to upgrade_package "python-mysql" + end + end + + context "postgresql backend" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + ::Chef::Recipe.any_instance.stub(:db).with("dashboard").and_return( + {"db_type" => "postgresql", "db_name" => "flying_elephant"}) + @chef_run.converge "openstack-dashboard::server" + end + + it "installs packages" do + expect(@chef_run).to upgrade_package "openstack-dashboard" + end + + it "installs postgresql packages" do + expect(@chef_run).to upgrade_package "python-psycopg2" + end + + it "creates local_settings.py" do + file = @chef_run.template "/usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py" + + expect(@chef_run).to create_file_with_content(file.name, "autogenerated") + end + + it "creates .blackhole dir with proper owner" do + dir = "/usr/share/openstack-dashboard/openstack_dashboard/.blackhole" + + expect(@chef_run.directory(dir)).to be_owned_by "root" + end + + it "creates an openstack-dashboard virtual host with proper DocRoot" do + # XXX this should be hardcoded to /etc/apache2/... , but the + # upstream cookbook is broken for SUSE + # see for e.g. http://tickets.opscode.com/browse/COOK-2434 + file = @chef_run.template "#{@chef_run.node["apache"]["dir"]}/conf.d/openstack-dashboard.conf" + + expect(@chef_run).to create_file_with_content(file.name, + "DocumentRoot /usr/share/openstack-dashboard/openstack_dashboard/.blackhole/") + end + end + end +end diff --git a/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb new file mode 100644 index 0000000..b437d1e --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/server-redhat_spec.rb @@ -0,0 +1,138 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::server" do + before { dashboard_stubs } + + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-dashboard::server" + end + + it "executes set-selinux-permissive" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, true) + chef_run.converge "openstack-dashboard::server" + cmd = "/sbin/setenforce Permissive" + + expect(chef_run).to execute_command cmd + end + + it "installs packages" do + expect(@chef_run).to upgrade_package "openstack-dashboard" + expect(@chef_run).to upgrade_package "MySQL-python" + end + + it "executes set-selinux-enforcing" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, true) + chef_run.converge "openstack-dashboard::server" + cmd = "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" + + expect(chef_run).to execute_command cmd + end + + describe "local_settings" do + before do + @file = @chef_run.template "/etc/openstack-dashboard/local_settings" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "rh specific template" do + expect(@chef_run).to create_file_with_content @file.name, "WEBROOT" + end + end + + describe "certs" do + before do + @crt = @chef_run.cookbook_file "/etc/pki/tls/certs/horizon.pem" + @key = @chef_run.cookbook_file "/etc/pki/tls/private/horizon.key" + end + + it "has proper owner" do + expect(@crt).to be_owned_by "root", "root" + expect(@key).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @crt.mode)).to eq "644" + expect(sprintf("%o", @key.mode)).to eq "640" + end + + it "notifies restore-selinux-context" do + expect(@crt).to notify "execute[restore-selinux-context]", :run + expect(@key).to notify "execute[restore-selinux-context]", :run + end + end + + describe "openstack-dashboard virtual host" do + before do + f = "/etc/httpd/conf.d/openstack-dashboard" + @file = @chef_run.template f + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "sets the ServerName directive " do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| + n.set["openstack"]["dashboard"]["server_hostname"] = "spec-test-host" + end + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).to create_file_with_content @file.name, "spec-test-host" + end + + it "notifies restore-selinux-context" do + expect(@file).to notify "execute[restore-selinux-context]", :run + end + end + + it "deletes openstack-dashboard.conf" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, true) + chef_run.converge "openstack-dashboard::server" + file = "/etc/httpd/conf.d/openstack-dashboard.conf" + + expect(chef_run).to delete_file file + end + + it "does not remove openstack-dashboard-ubuntu-theme package" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).not_to purge_package "openstack-dashboard-ubuntu-theme" + end + + it "doesn't remove default apache site" do + pending "TODO: how to properly test this" + end + + it "doesn't execute restore-selinux-context" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + + expect(chef_run).not_to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-dashboard/spec/server_spec.rb b/chef/cookbooks/openstack-dashboard/spec/server_spec.rb new file mode 100644 index 0000000..a7c5b98 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/server_spec.rb @@ -0,0 +1,238 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::server" do + before { dashboard_stubs } + + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-dashboard::server" + end + + it "doesn't execute set-selinux-permissive" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + cmd = "/sbin/setenforce Permissive" + + expect(chef_run).not_to execute_command cmd + end + + it "installs apache packages" do + expect(@chef_run).to include_recipe "apache2" + expect(@chef_run).to include_recipe "apache2::mod_wsgi" + expect(@chef_run).to include_recipe "apache2::mod_rewrite" + expect(@chef_run).to include_recipe "apache2::mod_ssl" + end + + it "doesn't execute set-selinux-enforcing" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + cmd = "/sbin/setenforce Enforcing ; restorecon -R /etc/httpd" + + expect(chef_run).not_to execute_command cmd + end + + it "installs packages" do + expect(@chef_run).to upgrade_package "lessc" + expect(@chef_run).to upgrade_package "openstack-dashboard" + expect(@chef_run).to upgrade_package "python-mysqldb" + end + + describe "local_settings.py" do + before do + @file = @chef_run.template "/etc/openstack-dashboard/local_settings.py" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has the customer banner" do + expect(@chef_run).to create_file_with_content @file.name, "autogenerated" + end + + it "has the memcached servers" do + expect(@chef_run).to create_file_with_content @file.name, "hostA" + end + + it "does not configure caching when backend == memcache and no servers provided" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + ::Chef::Recipe.any_instance.stub(:memcached_servers). + and_return nil + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).not_to create_file_with_content @file.name, + "django.core.cache.backends.memcached.MemcachedCache" + end + + it "does not configure caching when memcache_servers is empty" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + ::Chef::Recipe.any_instance.stub(:memcached_servers). + and_return [] + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).not_to create_file_with_content @file.name, + "django.core.cache.backends.memcached.MemcachedCache" + end + + it "has some plugins enabled" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["dashboard"]["plugins"] = ["testPlugin1" ] + end + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).to create_file_with_content @file.name, "testPlugin1" + end + + it "notifies apache2 restart" do + expect(@file).to notify "service[apache2]", :restart + end + + it "does not configure ssl proxy when ssl_offload is false" do + expect(@chef_run).not_to( + create_file_with_content @file.name, "SECURE_PROXY_SSL_HEADER") + end + + it "configures ssl proxy when ssl_offload is set to true" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["dashboard"]["ssl_offload"] = true + end + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).to( + create_file_with_content @file.name, "SECURE_PROXY_SSL_HEADER") + end + end + + it "executes openstack-dashboard syncdb" do + cmd = "python manage.py syncdb --noinput" + expect(@chef_run).to execute_command(cmd).with( + :cwd => "/usr/share/openstack-dashboard", + :environment => { + "PYTHONPATH" => "/etc/openstack-dashboard:" \ + "/usr/share/openstack-dashboard:" \ + "$PYTHONPATH" + } + ) + end + + describe "certs" do + before do + @crt = @chef_run.cookbook_file "/etc/ssl/certs/horizon.pem" + @key = @chef_run.cookbook_file "/etc/ssl/private/horizon.key" + end + + it "has proper owner" do + expect(@crt).to be_owned_by "root", "root" + expect(@key).to be_owned_by "root", "ssl-cert" + end + + it "has proper modes" do + expect(sprintf("%o", @crt.mode)).to eq "644" + expect(sprintf("%o", @key.mode)).to eq "640" + end + + it "notifies restore-selinux-context" do + expect(@crt).to notify "execute[restore-selinux-context]", :run + expect(@key).to notify "execute[restore-selinux-context]", :run + end + end + + it "creates .blackhole dir with proper owner" do + dir = "/usr/share/openstack-dashboard/openstack_dashboard/.blackhole" + + expect(@chef_run.directory(dir)).to be_owned_by "root" + end + + describe "openstack-dashboard virtual host" do + before do + f = "/etc/apache2/sites-available/openstack-dashboard" + @file = @chef_run.template f + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has the default banner" do + expect(@chef_run).to create_file_with_content @file.name, "autogenerated" + end + + it "has the default DocRoot" do + expect(@chef_run).to create_file_with_content @file.name, + "DocumentRoot /usr/share/openstack-dashboard/openstack_dashboard/.blackhole/" + end + + it "sets the ServerName directive " do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["dashboard"]["server_hostname"] = "spec-test-host" + end + chef_run.converge "openstack-dashboard::server" + + expect(chef_run).to create_file_with_content @file.name, "spec-test-host" + end + + it "notifies restore-selinux-context" do + expect(@file).to notify "execute[restore-selinux-context]", :run + end + end + + it "does not delete openstack-dashboard.conf" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + file = "/etc/httpd/conf.d/openstack-dashboard.conf" + + expect(chef_run).not_to delete_file file + end + + it "removes openstack-dashboard-ubuntu-theme package" do + expect(@chef_run).to purge_package "openstack-dashboard-ubuntu-theme" + end + + it "removes default virtualhost" do + opts = ::UBUNTU_OPTS.merge(:step_into => ["apache_site"]) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-dashboard::server" + cmd = "/usr/sbin/a2dissite 000-default" + + expect(chef_run).to execute_command cmd + end + + it "enables virtualhost" do + opts = ::UBUNTU_OPTS.merge(:step_into => ["apache_site"]) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-dashboard::server" + cmd = "/usr/sbin/a2ensite openstack-dashboard" + + expect(chef_run).to execute_command cmd + end + + it "notifies apache2 restart" do + pending "TODO: how to test when tied to an LWRP" + end + + it "doesn't execute restore-selinux-context" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command(/.*/, false) + chef_run.converge "openstack-dashboard::server" + cmd = "restorecon -Rv /etc/httpd /etc/pki; chcon -R -t httpd_sys_content_t /usr/share/openstack-dashboard || :" + + expect(chef_run).not_to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb b/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb new file mode 100644 index 0000000..6506821 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/spec/spec_helper.rb @@ -0,0 +1,30 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::FEDORA_OPTS = { + :platform => "fedora", + :version => "18", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} + +def dashboard_stubs + ::Chef::Recipe.any_instance.stub(:memcached_servers). + and_return ["hostA:port", "hostB:port"] + ::Chef::Recipe.any_instance.stub(:db_password).with("horizon"). + and_return "test-pass" +end diff --git a/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb b/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb new file mode 100644 index 0000000..359d59b --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/templates/default/dash-site.erb @@ -0,0 +1,60 @@ +<%= node["openstack"]["dashboard"]["custom_template_banner"] %> + +<% if node["openstack"]["dashboard"]["server_hostname"] -%> + ServerName <%= node["openstack"]["dashboard"]["server_hostname"] %> +<% end -%> +<% if node["openstack"]["dashboard"]["use_ssl"] %> + RewriteEngine On + RewriteCond %{HTTPS} off + RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R] + + +WSGISocketPrefix run/wsgi + + +<% if node["openstack"]["dashboard"]["server_hostname"] -%> + ServerName <%= node["openstack"]["dashboard"]["server_hostname"] %> +<% end -%> +<% end %> + ServerAdmin <%= node["apache"]["contact"] %> + WSGIScriptAlias / <%= node["openstack"]["dashboard"]["wsgi_path"] %> + WSGIDaemonProcess dashboard user=<%= node["apache"]["user"] %> group=<%= node["apache"]["group"] %> processes=3 threads=10 python-path=<%= node["openstack"]["dashboard"]["dash_path"] %> + WSGIProcessGroup dashboard + + DocumentRoot <%= node["openstack"]["dashboard"]["dash_path"] %>/.blackhole/ + Alias /static <%= node["openstack"]["dashboard"]["static_path"] %> + + + Options FollowSymLinks + AllowOverride None + + + > + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + + + > + Options FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + + + <% if node["openstack"]["dashboard"]["use_ssl"] %> + SSLEngine on + SSLCertificateFile <%= @ssl_cert_file %> + SSLCertificateKeyFile <%= @ssl_key_file %> + <% end %> + + # Allow custom files to overlay the site (such as logo.png) + RewriteEngine On + RewriteCond /opt/dash/site_overlay%{REQUEST_FILENAME} -s + RewriteRule ^/(.+) /opt/dash/site_overlay/$1 [L] + + ErrorLog <%= node["apache"]["log_dir"] %>/<%= node["openstack"]["dashboard"]["error_log"] %> + LogLevel warn + CustomLog <%= node["apache"]["log_dir"] %>/<%= node["openstack"]["dashboard"]["access_log"] %> combined + diff --git a/chef/cookbooks/openstack-dashboard/templates/default/default_stylesheets.html.erb b/chef/cookbooks/openstack-dashboard/templates/default/default_stylesheets.html.erb new file mode 100644 index 0000000..66c40a6 --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/templates/default/default_stylesheets.html.erb @@ -0,0 +1,7 @@ +{% load compress %} + +{% compress css %} + +{% endcompress %} + + diff --git a/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb b/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb new file mode 100644 index 0000000..55de5fe --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/templates/default/local_settings.py.erb @@ -0,0 +1,257 @@ +<%= node["openstack"]["dashboard"]["custom_template_banner"] %> + +import os + +from django.utils.translation import ugettext_lazy as _ + +from openstack_dashboard import exceptions + +DEBUG = <%= node["openstack"]["dashboard"]["debug"] ? "True" : "False" %> +TEMPLATE_DEBUG = DEBUG + +<% if %w(fedora redhat centos scientific).include? node.platform -%> +WEBROOT='' +LOGIN_URL = WEBROOT+'/auth/login/' +LOGOUT_URL = WEBROOT+'/auth/logout/' +#LOGIN_REDIRECT_URL = WEBROOT+'/syspanel' +LOGIN_REDIRECT_URL = WEBROOT+'/' +<% end %> + +<% if node["openstack"]["dashboard"]["ssl_offload"] %> +# Set SSL proxy settings: +# For Django 1.4+ pass this header from the proxy after terminating the SSL, +# and don't forget to strip it from the client's request. +# For more information see: +# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') +<% end %> + +# If Horizon is being served through SSL, then uncomment the following two +# settings to better secure the cookies from security exploits +#CSRF_COOKIE_SECURE = True +#SESSION_COOKIE_SECURE = True + +# Default OpenStack Dashboard configuration. +HORIZON_CONFIG = { + 'dashboards': ('project', 'admin', 'settings',), + 'default_dashboard': 'project', + 'user_home': 'openstack_dashboard.views.get_user_home', + 'ajax_queue_limit': 10, + 'auto_fade_alerts': { + 'delay': 3000, + 'fade_duration': 1500, + 'types': ['alert-success', 'alert-info'] + }, + 'help_url': "http://docs.openstack.org", + 'exceptions': {'recoverable': exceptions.RECOVERABLE, + 'not_found': exceptions.NOT_FOUND, + 'unauthorized': exceptions.UNAUTHORIZED}, +} + +# Specify a regular expression to validate user passwords. +# HORIZON_CONFIG["password_validator"] = { +# "regex": '.*', +# "help_text": _("Your password does not meet the requirements.") +# } + +# Disable simplified floating IP address management for deployments with +# multiple floating IP pools or complex network requirements. +# HORIZON_CONFIG["simple_ip_management"] = False + +# Turn off browser autocompletion for the login form if so desired. +# HORIZON_CONFIG["password_autocomplete"] = "off" + +LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) + +# Set custom secret key: +# You can either set it to a specific value or you can let horizion generate a +# default secret key that is unique on this machine, e.i. regardless of the +# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there +# may be situations where you would want to set this explicitly, e.g. when +# multiple dashboard instances are distributed on different machines (usually +# behind a load-balancer). Either you have to make sure that a session gets all +# requests routed to the same dashboard instance or you set the same SECRET_KEY +# for all of them. +# from horizon.utils import secret_key +# SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, '.secret_key_store')) + +# We recommend you use memcached for development; otherwise after every reload +# of the django development server, you will have to login again. To use +# memcached set CACHE_BACKED to something like 'memcached://127.0.0.1:11211/' +<% case node["openstack"]["dashboard"]["session_backend"] + when "file" %> +SESSION_ENGINE = 'django.contrib.sessions.backends.file' +<% when "memcached" + if @memcached_servers && !@memcached_servers.empty? +%> +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': [ +<% @memcached_servers.each do |address| %> + '<%= address %>', +<% end %> + ] + } +} +<% end + when "sql" +%> +SESSION_ENGINE = 'django.contrib.sessions.backends.db' +<% end %> + +# Send email to the console by default +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +# Or send them to /dev/null +#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' + +# Configure these for your outgoing email host +# EMAIL_HOST = 'smtp.my-company.com' +# EMAIL_PORT = 25 +# EMAIL_HOST_USER = 'djangomail' +# EMAIL_HOST_PASSWORD = 'top-secret!' + +# For multiple regions uncomment this configuration, and add (endpoint, title). +# AVAILABLE_REGIONS = [ +# ('http://cluster1.example.com:5000/v2.0', 'cluster1'), +# ('http://cluster2.example.com:5000/v2.0', 'cluster2'), +# ] + +OPENSTACK_KEYSTONE_URL = "<%= @auth_uri %>" +OPENSTACK_KEYSTONE_ADMIN_URL = "<%= @auth_admin_uri %>" +OPENSTACK_KEYSTONE_DEFAULT_ROLE = "<%= node["openstack"]["dashboard"]["keystone_default_role"] %>" + +# Disable SSL certificate checks (useful for self-signed certificates): +# OPENSTACK_SSL_NO_VERIFY = True + +# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the +# capabilities of the auth backend for Keystone. +# If Keystone has been configured to use LDAP as the auth backend then set +# can_edit_user to False and name to 'ldap'. +# +# TODO(tres): Remove these once Keystone has an API to identify auth backend. +OPENSTACK_KEYSTONE_BACKEND = { + 'name': 'native', + 'can_edit_user': True, + 'can_edit_project': True +} + +OPENSTACK_HYPERVISOR_FEATURES = { + 'can_set_mount_point': True, + + # NOTE: as of Grizzly this is not yet supported in Nova so enabling this + # setting will not do anything useful + 'can_encrypt_volumes': False +} + +# The OPENSTACK_QUANTUM_NETWORK settings can be used to enable optional +# services provided by quantum. Currently only the load balancer service +# is available. +OPENSTACK_QUANTUM_NETWORK = { + 'enable_lb': False +} + +# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints +# in the Keystone service catalog. Use this setting when Horizon is running +# external to the OpenStack environment. The default is 'internalURL'. +#OPENSTACK_ENDPOINT_TYPE = "publicURL" + +# The number of objects (Swift containers/objects or images) to display +# on a single page before providing a paging element (a "more" link) +# to paginate results. +API_RESULT_LIMIT = 1000 +API_RESULT_PAGE_SIZE = 20 + +# The timezone of the server. This should correspond with the timezone +# of your entire OpenStack installation, and hopefully be in UTC. +TIME_ZONE = "UTC" + +LOGGING = { + 'version': 1, + # When set to True this will disable all logging except + # for loggers specified in this configuration dictionary. Note that + # if nothing is specified here and disable_existing_loggers is True, + # django.db.backends will still log unless it is disabled explicitly. + 'disable_existing_loggers': False, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'django.utils.log.NullHandler', + }, + 'console': { + # Set the level to "DEBUG" for verbose output logging. + 'level': '<%= node["openstack"]["dashboard"]["debug"] ? "DEBUG" : "INFO" %>', + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + # Logging from django.db.backends is VERY verbose, send to null + # by default. + 'django.db.backends': { + 'handlers': ['null'], + 'propagate': False, + }, + 'requests': { + 'handlers': ['null'], + 'propagate': False, + }, + 'horizon': { + 'handlers': ['console'], + 'propagate': False, + }, + 'openstack_dashboard': { + 'handlers': ['console'], + 'propagate': False, + }, + 'novaclient': { + 'handlers': ['console'], + 'propagate': False, + }, + 'keystoneclient': { + 'handlers': ['console'], + 'propagate': False, + }, + 'glanceclient': { + 'handlers': ['console'], + 'propagate': False, + }, + 'nose.plugins.manager': { + 'handlers': ['console'], + 'propagate': False, + } + } +} + +<% django_backends = {'mysql' => 'mysql', + 'postgresql' => 'postgresql_psycopg2'} + engine = django_backends[@db_info['db_type']] %> + +# A dictionary containing the settings for all databases to be used with +# Django. It is a nested dictionary whose contents maps database aliases +# to a dictionary containing the options for an individual database. +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.<%= engine %>', + 'NAME': '<%= @db_info["db_name"] %>', + 'USER': '<%= node["openstack"]["dashboard"]["db"]["username"] %>', + 'PASSWORD': '<%= @db_pass %>', + 'HOST': '<%= @db_info["host"] %>', + 'default-character-set': 'utf8' + }, +} + +# Boolean that decides if compression should also be done outside of the +# request/response loop - independent from user requests. This allows to +# pre-compress CSS and JavaScript files and works just like the automatic +# compression with the {% compress %} tag. +COMPRESS_OFFLINE = True + +# Add additional plugins. +<% if node["openstack"]["dashboard"]["plugins"] %> +import sys +mod = sys.modules['openstack_dashboard.settings'] +<% node["openstack"]["dashboard"]["plugins"].each do |p| %> +mod.INSTALLED_APPS += ('<%= p %>', ) +<% end %> +<% end %> diff --git a/chef/cookbooks/openstack-dashboard/templates/default/rs_stylesheets.html.erb b/chef/cookbooks/openstack-dashboard/templates/default/rs_stylesheets.html.erb new file mode 100644 index 0000000..5995f0b --- /dev/null +++ b/chef/cookbooks/openstack-dashboard/templates/default/rs_stylesheets.html.erb @@ -0,0 +1,7 @@ +{% load compress %} + +{% compress css %} + +{% endcompress %} + + diff --git a/chef/cookbooks/openstack-identity/.tailor b/chef/cookbooks/openstack-identity/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-identity/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-identity/Berksfile b/chef/cookbooks/openstack-identity/Berksfile new file mode 100644 index 0000000..84e5b6d --- /dev/null +++ b/chef/cookbooks/openstack-identity/Berksfile @@ -0,0 +1,4 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-identity/Berksfile.lock b/chef/cookbooks/openstack-identity/Berksfile.lock new file mode 100644 index 0000000..368e102 --- /dev/null +++ b/chef/cookbooks/openstack-identity/Berksfile.lock @@ -0,0 +1,37 @@ +{ + "sha": "591cb6e4f1ccfb699c80c54dca3009a15e14b06f", + "sources": { + "openstack-identity": { + "path": "." + }, + "openstack-common": { + "locked_version": "0.3.0", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "ae80d36e8f8d5705e01bb6c14238eccb5450a229" + }, + "apt": { + "locked_version": "2.0.0" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-identity/Gemfile b/chef/cookbooks/openstack-identity/Gemfile new file mode 100644 index 0000000..04ef97e --- /dev/null +++ b/chef/cookbooks/openstack-identity/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-identity/Gemfile.lock b/chef/cookbooks/openstack-identity/Gemfile.lock new file mode 100644 index 0000000..2d00f7d --- /dev/null +++ b/chef/cookbooks/openstack-identity/Gemfile.lock @@ -0,0 +1,223 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.0.0) + i18n (~> 0.6, >= 0.6.4) + minitest (~> 4.2) + multi_json (~> 1.3) + thread_safe (~> 0.1) + tzinfo (~> 0.3.37) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + atomic (1.1.10) + berkshelf (2.0.5) + activesupport (>= 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.8.4) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.4) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.7) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rak (1.4) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.3) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.6.0) + strainer (3.0.3) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + thread_safe (0.1.0) + atomic + timers (1.1.0) + tins (0.8.2) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + tzinfo (0.3.37) + uuidtools (2.1.4) + varia_model (0.1.0) + buff-extensions (~> 0.1) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-identity/README.md b/chef/cookbooks/openstack-identity/README.md new file mode 100644 index 0000000..be43628 --- /dev/null +++ b/chef/cookbooks/openstack-identity/README.md @@ -0,0 +1,290 @@ +Description +=========== + +This cookbook installs the OpenStack Identity Service **Keystone** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Keystone is installed from packages, creating the default user, tenant, and roles. It also registers the identity service and identity endpoint. + +http://keystone.openstack.org/ + +Requirements +============ + +Chef 0.10.0 or higher required (for Chef environment use) + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* openstack-common + +Usage +===== + +server +------ + +Installs and Configures Keystone Service + +```json +"run_list": [ + "recipe[openstack-identity::server]" +] +``` + +Resources/Providers +=================== + +These resources provide an abstraction layer for interacting with the keystone server's API, allowing for other nodes to register any required users, tenants, roles, services, or endpoints. + +register +-------- + +Register users, tenants, roles, services and endpoints with Keystone + +### Actions + +- :create_tenant: Create a tenant +- :create_user: Create a user for a specified tenant +- :create_role: Create a role +- :grant_role: Grant a role to a specified user for a specified tenant +- :create_service: Create a service +- :create_endpoint: Create an endpoint for a sepcified service + +### General Attributes + +- auth_protocol: Required communication protocol with Keystone server + - Acceptable values are [ "http", "https" ] +- auth_host: Keystone server IP Address +- auth_port: Port Keystone server is listening on +- api_ver: API Version for Keystone server + - Accepted values are [ "/v2.0" ] +- auth_token: Auth Token for communication with Keystone server + +### :create_tenant Specific Attributes + +- tenant_name: Name of tenant to create +- tenant_description: Description of tenant to create +- tenant_enabled: Enable or Disable tenant + - Accepted values are [ "true", "false" ] + - Default is "true" + +### :create_user Specific Attributes + +- user_name: Name of user account to create +- user_pass: Password for the user account +- user_enabled: Enable or Disable user + - Accepted values are [ "true", "false" ] + - Default is "true" +- tenant_name: Name of tenant to create user in + +### :create_role Specific Attributes + +- role_name: Name of the role to create + +### :grant_role Specific Attributes + +- role_name: Name of the role to grant +- user_name: User name to grant the role to +- tenant_name: Name of tenant to grant role in + +### :create_service Specific Attributes + +- service_name: Name of service +- service_description: Description of service +- service_type: Type of service to create + - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume" ] + +### :create_endpoint Specific Attributes + +- endpoint_region: Default value is "RegionOne" +- endpoint_adminurl: URL to admin endpoint (using admin port) +- endpoint_internalurl: URL to service endpoint (using service port) +- endpoint_publicurl: URL to public endpoint + - Default is same as endpoint_internalURL +- service_type: Type of service to create endpoint for + - Accepted values are [ "image", "identity", "compute", "storage", "ec2", "volume" ] + +### Examples + + # Create 'openstack' tenant + openstack_identity_register "Register 'openstack' Tenant" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + tenant_name "openstack" + tenant_description "Default Tenant" + tenant_enabled "true" # Not required as this is the default + action :create_tenant + end + + # Create 'admin' user + openstack_identity_register "Register 'admin' User" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + tenant_name "openstack" + user_name "admin" + user_pass "secrete" + user_enabled "true" # Not required as this is the default + action :create_user + end + + # Create 'admin' role + openstack_identity_register "Register 'admin' Role" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + role_name role_key + action :create_role + end + + + # Grant 'admin' role to 'admin' user in the 'openstack' tenant + openstack_identity_register "Grant 'admin' Role to 'admin' User" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + tenant_name "openstack" + user_name "admin" + role_name "admin" + action :grant_role + end + + # Create 'identity' service + openstack_identity_register "Register Identity Service" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + service_name "keystone" + service_type "identity" + service_description "Keystone Identity Service" + action :create_service + end + + # Create 'identity' endpoint + openstack_identity_register "Register Identity Endpoint" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + service_type "identity" + endpoint_region "RegionOne" + endpoint_adminurl "http://192.168.1.10:35357/v2.0" + endpoint_internalurl "http://192.168.1.10:5001/v2.0" + endpoint_publicurl "http://1.2.3.4:5001/v2.0" + action :create_endpoint + end + +credentials +----------- + +Create EC2 credentials for a given user in the specified tenant + +### Actions + +- :create_ec2: create EC2 credentials + +### General Attributes + +- auth_protocol: Required communication protocol with Keystone server. Acceptable values are [ "http", "https" ] +- auth_host: Keystone server IP Address +- auth_port: Port Keystone server is listening on +- api_ver: API Version for Keystone server + - Accepted values are [ "/v2.0" ] +- auth_token: Auth Token for communication with Keystone server + +### :create_ec2 Specific Attributes + +- user_name: User name to grant the credentials for +- tenant_name: Tenant name to grant the credentials in + +### Examples + + openstack_identity_credentials "Create EC2 credentials for 'admin' user" do + auth_host "192.168.1.10" + auth_port "35357" + auth_protocol "http" + api_ver "/v2.0" + auth_token "123456789876" + user_name "admin" + tenant_name "openstack" + end + +Attributes +========== + +* `openstack['identity']['db_server_chef_role']` - The name of the Chef role that knows about the db server +* `openstack['identity']['bind_interface']` - Interface to bind keystone to +* `openstack['identity']['service_port']` - Port to listen on for client functions +* `openstack['identity']['admin_port']` - Port to listen on for admin functions +* `openstack['identity']['user']` - User keystone runs as +* `openstack['identity']['group']` - Group keystone runs as +* `openstack['identity']['db']` - Name of keystone database +* `openstack['identity']['db_user']` - Username for keystone database access +* `openstack['identity']['db_passwd']` - Password for keystone database access +* `openstack['identity']['db_ipaddress']` - IP address of the keystone database +* `openstack['identity']['api_ipaddress']` - IP address for the keystone API to bind to. _TODO_: Rename to bind_address +* `openstack['identity']['verbose']` - Enables/disables verbose output for keystone API server +* `openstack['identity']['debug']` - Enables/disables debug output for keystone API server +* `openstack['identity']['service_port']` - Port for the keystone service API to bind to +* `openstack['identity']['admin_port']` - Port for the keystone admin service to bind to +* `openstack['identity']['admin_token']` - Admin token for bootstraping keystone server +* `openstack['identity']['roles']` - Array of roles to create in the keystone server +* `openstack['identity']['users']` - Array of users to create in the keystone server + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +Author:: Justin Shepherd () +Author:: Jason Cannavale () +Author:: Ron Pedde () +Author:: Joseph Breu () +Author:: William Kelly () +Author:: Darren Birkett () +Author:: Evan Callicoat () +Author:: Matt Ray () +Author:: Jay Pipes () +Author:: John Dewey () +Author:: Sean Gallagher () +Author:: Ionut Artarisi () + +Copyright 2012, Rackspace US, Inc. +Copyright 2012-2013, Opscode, Inc. +Copyright 2012-2013, AT&T Services, Inc. +Copyright 2013, SUSE Linux GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-identity/Strainerfile b/chef/cookbooks/openstack-identity/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-identity/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-identity/attributes/default.rb b/chef/cookbooks/openstack-identity/attributes/default.rb new file mode 100644 index 0000000..1c90bf6 --- /dev/null +++ b/chef/cookbooks/openstack-identity/attributes/default.rb @@ -0,0 +1,133 @@ +# +# Cookbook Name:: openstack-identity +# Recipe:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["identity"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +# Adding these as blank +# this needs to be here for the initial deep-merge to work +default["credentials"]["EC2"]["admin"]["access"] = "" +default["credentials"]["EC2"]["admin"]["secret"] = "" + +default["openstack"]["identity"]["db"]["username"] = "keystone" +# Execute database migrations. There are cases where migrations should not be +# executed. For example when upgrading a zone, and the identity database is +# replicated across many zones. +default["openstack"]["identity"]["db"]["migrate"] = true + +default["openstack"]["identity"]["verbose"] = "False" +default["openstack"]["identity"]["debug"] = "False" + +default["openstack"]["identity"]["service_port"] = "5000" +default["openstack"]["identity"]["admin_port"] = "35357" +default["openstack"]["identity"]["region"] = "RegionOne" + +default["openstack"]["identity"]["bind_interface"] = "lo" + +# Logging stuff +default["openstack"]["identity"]["syslog"]["use"] = false +default["openstack"]["identity"]["syslog"]["facility"] = "LOG_LOCAL2" +default["openstack"]["identity"]["syslog"]["config_facility"] = "local2" + +# default["openstack"]["identity"]["roles"] = [ "admin", "Member", "KeystoneAdmin", "KeystoneServiceAdmin", "sysadmin", "netadmin" ] +default["openstack"]["identity"]["roles"] = [ "admin", "Member", "KeystoneAdmin", "KeystoneServiceAdmin" ] + +#TODO(shep): this should probably be derived from keystone.users hash keys +default["openstack"]["identity"]["tenants"] = [ "admin", "service"] + +default["openstack"]["identity"]["admin_user"] = "admin" +default["openstack"]["identity"]["admin_tenant_name"] = "admin" + +default["openstack"]["identity"]["users"] = { + default["openstack"]["identity"]["admin_user"] => { + "default_tenant" => default["openstack"]["identity"]["admin_tenant_name"], + "roles" => { + "admin" => [ "admin" ], + "KeystoneAdmin" => [ "admin" ], + "KeystoneServiceAdmin" => [ "admin" ] + } + }, + "monitoring" => { + "password" => "", + "default_tenant" => "service", + "roles" => { + "Member" => [ "admin" ] + } + } +} + +# PKI signing. Corresponds to the [signing] section of keystone.conf +# Note this section is only written if node["openstack"]["auth"]["straegy"] == "pki" +default["openstack"]["identity"]["signing"]["basedir"] = "/etc/keystone/ssl" +default["openstack"]["identity"]["signing"]["certfile"] = "/etc/keystone/ssl/certs/signing_cert.pem" +default["openstack"]["identity"]["signing"]["keyfile"] = "/etc/keystone/ssl/private/signing_key.pem" +default["openstack"]["identity"]["signing"]["ca_certs"] = "/etc/keystone/ssl/certs/ca.pem" +default["openstack"]["identity"]["signing"]["key_size"] = "1024" +default["openstack"]["identity"]["signing"]["valid_days"] = "3650" +default["openstack"]["identity"]["signing"]["ca_password"] = nil + +# These switches set the various drivers for the different Keystone components +default["openstack"]["identity"]["identity"]["backend"] = "sql" +default["openstack"]["identity"]["token"]["backend"] = "sql" +default["openstack"]["identity"]["catalog"]["backend"] = "sql" + +# platform defaults +case platform +when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this + default["openstack"]["identity"]["user"] = "keystone" + default["openstack"]["identity"]["group"] = "keystone" + default["openstack"]["identity"]["platform"] = { + "mysql_python_packages" => [ "MySQL-python" ], + "postgresql_python_packages" => [ "python-psycopg2" ], + "memcache_python_packages" => [ "python-memcached" ], + "keystone_packages" => [ "openstack-keystone" ], + "keystone_service" => "openstack-keystone", + "keystone_process_name" => "keystone-all", + "package_options" => "" + } +when "suse" + default["openstack"]["identity"]["user"] = "openstack-keystone" + default["openstack"]["identity"]["group"] = "openstack-keystone" + default["openstack"]["identity"]["platform"] = { + "mysql_python_packages" => [ "python-mysql" ], + "postgresql_python_packages" => [ "python-psycopg2" ], + "memcache_python_packages" => [ "python-python-memcached" ], + "keystone_packages" => [ "openstack-keystone" ], + "keystone_service" => "openstack-keystone", + "keystone_process_name" => "keystone-all", + "package_options" => "" + } +when "ubuntu" + default["openstack"]["identity"]["user"] = "keystone" + default["openstack"]["identity"]["group"] = "keystone" + default["openstack"]["identity"]["platform"] = { + "mysql_python_packages" => [ "python-mysqldb" ], + "postgresql_python_packages" => [ "python-psycopg2" ], + "memcache_python_packages" => [ "python-memcache" ], + "keystone_packages" => [ "keystone" ], + "keystone_service" => "keystone", + "keystone_process_name" => "keystone-all", + "package_options" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py b/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py new file mode 100644 index 0000000..3c97035 --- /dev/null +++ b/chef/cookbooks/openstack-identity/files/default/keystone_plugin.py @@ -0,0 +1,96 @@ +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from keystoneclient.v2_0 import Client as KeystoneClient + +import collectd + +global NAME, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING + +NAME = "keystone_plugin" +OS_USERNAME = "username" +OS_PASSWORD = "password" +OS_TENANT_NAME = "tenantname" +OS_AUTH_URL = "http://localhost:5000/v2.0" +VERBOSE_LOGGING = False + + +def get_stats(user, passwd, tenant, url): + keystone = KeystoneClient(username=user, password=passwd, tenant_name=tenant, auth_url=url) + data = dict() + + # Define list of keys to query for + keys = ('tenants','users','roles','services','endpoints') + for key in keys: + data["openstack.keystone.%s.count" % key] = len(keystone.__getattribute__(key).list()) + + tenant_list = keystone.tenants.list() + for tenant in tenant_list: + data["openstack.keystone.tenants.tenants.%s.users.count" % tenant.name] = len(keystone.tenants.list_users(tenant.id)) + + ########## + # debug + #for key in data.keys(): + # print "%s = %s" % (key, data[key]) + ########## + + return data + +def configure_callback(conf): + """Received configuration information""" + global OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, VERBOSE_LOGGING + for node in conf.children: + if node.key == "Username": + OS_USERNAME = node.values[0] + elif node.key == "Password": + OS_PASSWORD = node.values[0] + elif node.key == "TenantName": + OS_TENANT_NAME = node.values[0] + elif node.key == "AuthURL": + OS_AUTH_URL = node.values[0] + elif node.key == "Verbose": + VERBOSE_LOGGING = node.values[0] + else: + logger("warn", "Unknown config key: %s" % node.key) + + +def read_callback(): + logger("verb", "read_callback") + info = get_stats(OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL) + + if not info: + logger("err", "No information received") + return + + for key in info.keys(): + logger('verb', 'Dispatching %s : %i' % (key, int(info[key]))) + val = collectd.Values(plugin=key) + val.type = 'gauge' + val.values = [int(info[key])] + val.dispatch() + + +def logger(t, msg): + if t == 'err': + collectd.error('%s: %s' % (NAME, msg)) + if t == 'warn': + collectd.warning('%s: %s' % (NAME, msg)) + elif t == 'verb' and VERBOSE_LOGGING == True: + collectd.info('%s: %s' % (NAME, msg)) + +collectd.register_config(configure_callback) +collectd.warning("Initializing keystone plugin") +collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-identity/metadata.rb b/chef/cookbooks/openstack-identity/metadata.rb new file mode 100644 index 0000000..cdfc4ee --- /dev/null +++ b/chef/cookbooks/openstack-identity/metadata.rb @@ -0,0 +1,16 @@ +name "openstack-identity" +maintainer "Opscode, Inc." +maintainer_email "matt@opscode.com" +license "Apache 2.0" +description "The OpenStack Identity service Keystone." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.0" + +recipe "openstack-identity::server", "Installs and Configures Keystone Service" +recipe "openstack-identity::registration", "Adds user, tenant, role and endpoint records to Keystone" + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "openstack-common", "~> 0.4.0" diff --git a/chef/cookbooks/openstack-identity/providers/register.rb b/chef/cookbooks/openstack-identity/providers/register.rb new file mode 100644 index 0000000..2e6fa09 --- /dev/null +++ b/chef/cookbooks/openstack-identity/providers/register.rb @@ -0,0 +1,301 @@ +# +# Cookbook Name:: openstack-identity +# Provider:: register +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Opscode, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut +include ::Openstack + +private +def generate_creds resource + { + 'OS_SERVICE_ENDPOINT' => resource.auth_uri, + 'OS_SERVICE_TOKEN' => resource.bootstrap_token + } +end + +private +def identity_command resource, cmd, args={} + keystonecmd = ['keystone'] << cmd + args.each { |key, val| + keystonecmd << "--#{key}" << val.to_s + } + Chef::Log.debug("Running identity command: #{keystonecmd}") + rc = shell_out(keystonecmd, :env => generate_creds(resource)) + if rc.exitstatus != 0 + raise RuntimeError, "#{rc.stderr} (#{rc.exitstatus})" + end + rc.stdout +end + +private +def identity_uuid resource, type, key, value, args={}, uuid_field='id' + begin + output = identity_command resource, "#{type}-list", args + output = prettytable_to_array(output) + output.each { |obj| + if obj.has_key?(uuid_field) and obj[key] == value + return obj[uuid_field] + end + } + rescue RuntimeError => e + raise RuntimeError, "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}" + end + nil +end + +action :create_service do + if node["openstack"]["identity"]["catalog"]["backend"] == "templated" + Chef::Log.info("Skipping service creation - templated catalog backend in use.") + new_resource.updated_by_last_action(false) + else + begin + service_uuid = identity_uuid new_resource, "service", "type", new_resource.service_type + + unless service_uuid + identity_command new_resource, "service-create", + { 'type' => new_resource.service_type, + 'name' => new_resource.service_name, + 'description' => new_resource.service_description } + Chef::Log.info("Created service '#{new_resource.service_name}'") + new_resource.updated_by_last_action(true) + else + Chef::Log.info("Service Type '#{new_resource.service_type}' already exists.. Not creating.") + Chef::Log.info("Service UUID: #{service_uuid}") + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to create service '#{new_resource.service_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end + end +end + +action :create_endpoint do + if node["openstack"]["identity"]["catalog"]["backend"] == "templated" + Chef::Log.info("Skipping endpoint creation - templated catalog backend in use.") + new_resource.updated_by_last_action(false) + else + begin + service_uuid = identity_uuid new_resource, "service", "type", new_resource.service_type + unless service_uuid + Chef::Log.error("Unable to find service type '#{new_resource.service_type}'") + new_resource.updated_by_last_action(false) + next + end + + endpoint_uuid = identity_uuid new_resource, "endpoint", "service_id", service_uuid + unless endpoint_uuid + identity_command new_resource, "endpoint-create", + { 'region' => new_resource.endpoint_region, + 'service_id' => service_uuid, + 'publicurl' => new_resource.endpoint_publicurl, + 'internalurl' => new_resource.endpoint_internalurl, + 'adminurl' => new_resource.endpoint_adminurl } + Chef::Log.info("Created endpoint for service type '#{new_resource.service_type}'") + new_resource.updated_by_last_action(true) + else + Chef::Log.info("Endpoint already exists for Service Type '#{new_resource.service_type}' already exists.. Not creating.") + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to create endpoint for service type '#{new_resource.service_type}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end + end +end + +action :create_tenant do + begin + tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + + unless tenant_uuid + identity_command new_resource, "tenant-create", + { 'name' => new_resource.tenant_name, + 'description' => new_resource.tenant_description, + 'enabled' => new_resource.tenant_enabled } + Chef::Log.info("Created tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(true) + else + Chef::Log.info("Tenant '#{new_resource.tenant_name}' already exists.. Not creating.") + Chef::Log.info("Tenant UUID: #{tenant_uuid}") if tenant_uuid + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to create tenant '#{new_resource.tenant_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end +end + +action :create_role do + begin + role_uuid = identity_uuid new_resource, "role", "name", new_resource.role_name + + unless role_uuid + identity_command new_resource, "role-create", + { 'name' => new_resource.role_name } + Chef::Log.info("Created Role '#{new_resource.role_name}'") + new_resource.updated_by_last_action(true) + else + Chef::Log.info("Role '#{new_resource.role_name}' already exists.. Not creating.") + Chef::Log.info("Role UUID: #{role_uuid}") + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to create role '#{new_resource.role_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end +end + +action :create_user do + begin + tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + unless tenant_uuid + Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + next + end + + output = identity_command new_resource, "user-list", {'tenant-id' => tenant_uuid} + users = prettytable_to_array output + user_found = false + users.each { |user| + if user['name'] == new_resource.user_name + user_found = true + end + } + + if user_found + Chef::Log.info("User '#{new_resource.user_name}' already exists for tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + next + end + + identity_command new_resource, "user-create", + { 'name' => new_resource.user_name, + 'tenant-id' => tenant_uuid, + 'pass' => new_resource.user_pass, + 'enabled' => new_resource.user_enabled } + Chef::Log.info("Created user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(true) + rescue Exception => e + Chef::Log.error("Unable to create user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end +end + +action :grant_role do + begin + tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + unless tenant_uuid + Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + next + end + + user_uuid = identity_uuid new_resource, "user", "name", new_resource.user_name + unless tenant_uuid + Chef::Log.error("Unable to find user '#{new_resource.user_name}'") + new_resource.updated_by_last_action(false) + next + end + + role_uuid = identity_uuid new_resource, "role", "name", new_resource.role_name + unless tenant_uuid + Chef::Log.error("Unable to find role '#{new_resource.role_name}'") + new_resource.updated_by_last_action(false) + next + end + + assigned_role_uuid = identity_uuid new_resource, "user-role", "name", new_resource.role_name, + { 'tenant-id' => tenant_uuid, + 'user-id' => user_uuid } + unless role_uuid == assigned_role_uuid + identity_command new_resource, "user-role-add", + { 'tenant-id' => tenant_uuid, + 'role-id' => role_uuid, + 'user-id' => user_uuid } + Chef::Log.info("Granted Role '#{new_resource.role_name}' to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(true) + else + Chef::Log.info("Role '#{new_resource.role_name}' already granted to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to grant role '#{new_resource.role_name}' to user '#{new_resource.user_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end +end + +action :create_ec2_credentials do + begin + tenant_uuid = identity_uuid new_resource, "tenant", "name", new_resource.tenant_name + unless tenant_uuid + Chef::Log.error("Unable to find tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + next + end + + user_uuid = identity_uuid new_resource, "user", "name", new_resource.user_name, {'tenant-id' => tenant_uuid} + unless tenant_uuid + Chef::Log.error("Unable to find user '#{new_resource.user_name}'") + new_resource.updated_by_last_action(false) + next + end + + # this is not really a uuid, but this will work nonetheless + access = identity_uuid new_resource, "ec2-credentials", "tenant", new_resource.tenant_name, {'user-id' => user_uuid}, "access" + unless access + output = identity_command new_resource, "ec2-credentials-create", + { 'user-id' => user_uuid, + 'tenant-id' => tenant_uuid } + Chef::Log.info("Created EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") + data = prettytable_to_array(output) + + if data.length != 1 + Chef::Log.error("Got bad data when creating ec2 credentials for #{new_resource.user_name}") + Chef::Log.error("Data: #{data}") + else + # Update node attributes + node.set['credentials']['EC2'][new_resource.user_name]['access'] = data[0]['access'] + node.set['credentials']['EC2'][new_resource.user_name]['secret'] = data[0]['secret'] + node.save unless Chef::Config[:solo] + new_resource.updated_by_last_action(true) + end + else + Chef::Log.info("EC2 credentials already exist for '#{new_resource.user_name}' in tenant '#{new_resource.tenant_name}'") + new_resource.updated_by_last_action(false) + end + rescue Exception => e + Chef::Log.error("Unable to create EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'") + Chef::Log.error("Error was: #{e.message}") + new_resource.updated_by_last_action(false) + end +end + + + diff --git a/chef/cookbooks/openstack-identity/recipes/default.rb b/chef/cookbooks/openstack-identity/recipes/default.rb new file mode 100644 index 0000000..9c0e915 --- /dev/null +++ b/chef/cookbooks/openstack-identity/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: openstack-identity +# Recipe:: default +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/openstack-identity/recipes/registration.rb b/chef/cookbooks/openstack-identity/recipes/registration.rb new file mode 100644 index 0000000..5859222 --- /dev/null +++ b/chef/cookbooks/openstack-identity/recipes/registration.rb @@ -0,0 +1,174 @@ +# +# Cookbook Name:: openstack-identity +# Recipe:: setup +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" +identity_endpoint = endpoint "identity-api" + +admin_tenant_name = node["openstack"]["identity"]["admin_tenant_name"] +admin_user = node["openstack"]["identity"]["admin_user"] +admin_pass = user_password node["openstack"]["identity"]["admin_password"] +auth_uri = ::URI.decode identity_admin_endpoint.to_s + +bootstrap_token = secret "secrets", "#{node["openstack"]["identity"]["admin_token"]}" + +# We need to bootstrap the keystone admin user so that calls +# to keystone_register will succeed, since those provider calls +# use the admin tenant/user/pass to get an admin token. +bash "bootstrap-keystone-admin" do + # A shortcut bootstrap command was added to python-keystoneclient + # in early Grizzly timeframe... but we need to do all the commands + # here manually since the python-keystoneclient package included + # in CloudArchive (for now) doesn't have it... + insecure = node["openstack"]["auth"]["validate_certs"] ? "" : " --insecure" + base_ks_cmd = "keystone#{insecure} --endpoint=#{auth_uri} --token=#{bootstrap_token}" + code <<-EOF +set -x +function get_id () { + echo `"$@" | grep ' id ' | awk '{print $4}'` +} +#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} +if [[ $? -eq 1 ]]; then + ADMIN_TENANT=$(get_id #{base_ks_cmd} tenant-create --name=#{admin_tenant_name}) +else + ADMIN_TENANT=$(#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} | awk '{print $2}') +fi +#{base_ks_cmd} role-list | grep admin +if [[ $? -eq 1 ]]; then + ADMIN_ROLE=$(get_id #{base_ks_cmd} role-create --name=admin) +else + ADMIN_ROLE=$(#{base_ks_cmd} role-list | grep admin | awk '{print $2}') +fi +#{base_ks_cmd} user-list | grep #{admin_user} +if [[ $? -eq 1 ]]; then + ADMIN_USER=$(get_id #{base_ks_cmd} user-create --name=#{admin_user} --pass="#{admin_pass}" --email=#{admin_user}@example.com) +else + ADMIN_USER=$(#{base_ks_cmd} user-list | grep #{admin_user} | awk '{print $2}') +fi +#{base_ks_cmd} user-role-list --user-id=$ADMIN_USER --tenant-id=$ADMIN_TENANT | grep admin +if [[ $? -eq 1 ]]; then + #{base_ks_cmd} user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $ADMIN_TENANT +fi +exit 0 +EOF +end + +node["openstack"]["identity"]["tenants"].each do |tenant_name| + ## Add openstack tenant ## + openstack_identity_register "Register '#{tenant_name}' Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name tenant_name + tenant_description "#{tenant_name} Tenant" + tenant_enabled true # Not required as this is the default + + action :create_tenant + end +end + +node["openstack"]["identity"]["roles"].each do |role_key| + openstack_identity_register "Register '#{role_key.to_s}' Role" do + auth_uri auth_uri + bootstrap_token bootstrap_token + role_name role_key + + action :create_role + end +end + + +node['openstack']['services'].each_key do |service| + cu_user = node['openstack']['identity']["#{service}"]['username'] + cu_pass = node['openstack']['identity']["#{service}"]['password'] + cu_tenant = node['openstack']['identity']["#{service}"]['tenant'] + cu_role = node['openstack']['identity']["#{service}"]['role'] + + if "#{service}" != "identity" + openstack_identity_register "Register '#{service}' User" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name cu_user + user_pass cu_pass + tenant_name cu_tenant + user_enabled true # Not required as this is the default + action :create_user + end + + openstack_identity_register "Grant #{cu_role} Role to #{cu_user} User in #{cu_tenant} Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name cu_user + role_name cu_role + tenant_name cu_tenant + action :grant_role + end + end + + cu_service = node['openstack']['services']["#{service}"]['name'] + + openstack_identity_register "Register #{service} Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "#{cu_service}" + service_type "#{service}" + service_description "Openstack #{service} Service" + action :create_service + end + + if %Q/#{node['openstack']['services']["#{service}"]['status']}/ == "enable" + service_endpoint = endpoint "#{service}-api" + if service == "identity" or service == "compute-ec2" or service == "swift" + service_endpoint_admin = endpoint "#{service}-admin" + elsif + service_endpoint_admin = service_endpoint + end + node.set['openstack']["#{service}"]['adminURL'] = service_endpoint_admin.to_s + node.set['openstack']["#{service}"]['internalURL'] = service_endpoint.to_s + node.set['openstack']["#{service}"]['publicURL'] = service_endpoint.to_s + + openstack_identity_register "Register #{service} Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type "#{service}" + endpoint_region node["openstack"]["identity"]["region"] + endpoint_adminurl node['openstack']["#{service}"]['adminURL'] + endpoint_internalurl node['openstack']["#{service}"]['internalURL'] + endpoint_publicurl node['openstack']["#{service}"]['publicURL'] + action :create_endpoint + end + end +end + + +node["openstack"]["identity"]["users"].each do |username, user_info| + openstack_identity_register "Create EC2 credentials for '#{username}' user" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name username + tenant_name user_info["default_tenant"] + + action :create_ec2_credentials + end +end diff --git a/chef/cookbooks/openstack-identity/recipes/server.rb b/chef/cookbooks/openstack-identity/recipes/server.rb new file mode 100644 index 0000000..5517fc8 --- /dev/null +++ b/chef/cookbooks/openstack-identity/recipes/server.rb @@ -0,0 +1,174 @@ +# +# Cookbook Name:: openstack-identity +# Recipe:: server +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +if node["openstack"]["identity"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options = node["openstack"]["identity"]["platform"] + +##### NOTE ##### +# https://bugs.launchpad.net/ubuntu/+source/keystone/+bug/931236 +################ + +db_type = node['openstack']['db']['identity']['db_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :install + end +end + +platform_options["memcache_python_packages"].each do |pkg| + package pkg do + action :install + end +end + +platform_options["keystone_packages"].each do |pkg| + package pkg do + options platform_options["package_options"] + + action :upgrade + end +end + +execute "Keystone: sleep" do + command "sleep 10s" + + action :nothing +end + +service "keystone" do + service_name platform_options["keystone_service"] + supports :status => true, :restart => true + + action [ :enable ] + + notifies :run, "execute[Keystone: sleep]", :immediately +end + +directory "/etc/keystone" do + owner node["openstack"]["identity"]["user"] + group node["openstack"]["identity"]["group"] + mode 00700 +end + +directory node["openstack"]["identity"]["signing"]["basedir"] do + owner node["openstack"]["identity"]["user"] + group node["openstack"]["identity"]["group"] + mode 00700 + + only_if { node["openstack"]["auth"]["strategy"] == "pki" } +end + +file "/var/lib/keystone/keystone.db" do + action :delete +end + +execute "keystone-manage pki_setup" do + user node["openstack"]["identity"]["user"] + + only_if { node["openstack"]["auth"]["strategy"] == "pki" } + not_if { ::FileTest.exists? node["openstack"]["identity"]["signing"]["keyfile"] } +end + +identity_admin_endpoint = endpoint "identity-admin" +identity_endpoint = endpoint "identity-api" +compute_endpoint = endpoint "compute-api" +ec2_endpoint = endpoint "compute-ec2-api" +image_endpoint = endpoint "image-api" +network_endpoint = endpoint "network-api" +volume_endpoint = endpoint "volume-api" + +#db_user = node["openstack"]["identity"]["db"]["username"] +#db_pass = db_password "keystone" +db_user = node['openstack']['db']['identity']['username'] +db_pass = db_password node['openstack']['db']['identity']['password'] + +sql_connection = db_uri("identity", db_user, db_pass) + +bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" + +ip_address = address_for node["openstack"]["identity"]["bind_interface"] + +# If the search role is set, we search for memcache +# servers via a Chef search. If not, we look at the +# memcache.servers attribute. +memcache_servers = memcached_servers.join "," # from openstack-common lib + +uris = { + 'identity-admin' => identity_admin_endpoint.to_s.gsub('%25','%'), + 'identity' => identity_endpoint.to_s.gsub('%25','%'), + 'image' => image_endpoint.to_s.gsub('%25','%'), + 'compute' => compute_endpoint.to_s.gsub('%25','%'), + 'ec2' => ec2_endpoint.to_s.gsub('%25','%'), + 'network' => network_endpoint.to_s.gsub('%25','%'), + 'volume' => volume_endpoint.to_s.gsub('%25','%') +} + +# These configuration endpoints must not have the path (v2.0, etc) +# added to them, as these values are used in returning the version +# listing information from the root / endpoint. +ie = identity_endpoint +public_endpoint = "#{ie.scheme}://#{ie.host}:#{ie.port}/" +ae = identity_admin_endpoint +admin_endpoint = "#{ae.scheme}://#{ae.host}:#{ae.port}/" + +template "/etc/keystone/keystone.conf" do + source "keystone.conf.erb" + owner node["openstack"]["identity"]["user"] + group node["openstack"]["identity"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection, + :ip_address => ip_address, + "bootstrap_token" => bootstrap_token, + "memcache_servers" => memcache_servers, + "uris" => uris, + "public_endpoint" => public_endpoint, + "admin_endpoint" => admin_endpoint + ) + + notifies :restart, "service[keystone]", :immediately +end + +template "/etc/keystone/default_catalog.templates" do + source "default_catalog.templates.erb" + owner node["openstack"]["identity"]["user"] + group node["openstack"]["identity"]["group"] + mode 00644 + variables( + "uris" => uris + ) + + notifies :restart, "service[keystone]", :immediately + only_if { node["openstack"]["identity"]["catalog"]["backend"] == "templated" } +end + +# sync db after keystone.conf is generated +execute "keystone-manage db_sync" do + only_if { node["openstack"]["identity"]["db"]["migrate"] } +end diff --git a/chef/cookbooks/openstack-identity/resources/register.rb b/chef/cookbooks/openstack-identity/resources/register.rb new file mode 100644 index 0000000..2e6b086 --- /dev/null +++ b/chef/cookbooks/openstack-identity/resources/register.rb @@ -0,0 +1,61 @@ +# +# Cookbook Name:: openstack-identity +# Resource:: register +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :create_service, :create_endpoint, :create_tenant, :create_user, :create_role, :grant_role, :create_ec2_credentials + +# In earlier versions of Chef the LWRP DSL doesn't support specifying +# a default action, so you need to drop into Ruby. +def initialize(*args) + super + @action = :create +end + +Boolean = [TrueClass, FalseClass] + +attribute :auth_uri, :kind_of => String +attribute :bootstrap_token, :kind_of => String + +# Used by both :create_service and :create_endpoint +attribute :service_type, :kind_of => String, :equal_to => [ "image", "identity", "compute", "storage", "ec2", "volume", "object-store", "metering", "network" ] + +# :create_service specific attributes +attribute :service_name, :kind_of => String +attribute :service_description, :kind_of => String + +# :create_endpoint specific attributes +attribute :endpoint_region, :kind_of => String, :default => "RegionOne" +attribute :endpoint_adminurl, :kind_of => String +attribute :endpoint_internalurl, :kind_of => String +attribute :endpoint_publicurl, :kind_of => String + +# Used by both :create_tenant and :create_user +attribute :tenant_name, :kind_of => String + +# :create_tenant specific attributes +attribute :tenant_description, :kind_of => String +attribute :tenant_enabled, :kind_of => Boolean, :default => true + +# :create_user specific attributes +attribute :user_name, :kind_of => String +attribute :user_pass, :kind_of => String +# attribute :user_email, :kind_of => String +attribute :user_enabled, :kind_of => Boolean, :default => true + +# Used by :create_role and :grant_role specific attributes +attribute :role_name, :kind_of => String diff --git a/chef/cookbooks/openstack-identity/spec/default_spec.rb b/chef/cookbooks/openstack-identity/spec/default_spec.rb new file mode 100644 index 0000000..808fe79 --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/default_spec.rb @@ -0,0 +1,4 @@ +require_relative "spec_helper" + +describe "openstack-identity::default" do +end diff --git a/chef/cookbooks/openstack-identity/spec/register_spec.rb b/chef/cookbooks/openstack-identity/spec/register_spec.rb new file mode 100644 index 0000000..04e6a3b --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/register_spec.rb @@ -0,0 +1,215 @@ +require_relative "spec_helper" + +describe Chef::Provider::Execute do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-identity::default" + @node = @chef_run.node + @node.set["openstack"] = { + "identity" => { + "catalog" => { + "backend" => "sql" + } + } + } + @cookbook_collection = Chef::CookbookCollection.new([]) + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) + + @tenant_resource = Chef::Resource::OpenstackIdentityRegister.new("tenant1", @run_context) + @tenant_resource.tenant_name "tenant1" + @tenant_resource.tenant_description "tenant1 Tenant" + + @service_resource = Chef::Resource::OpenstackIdentityRegister.new("service1", @run_context) + @service_resource.service_type "compute" + @service_resource.service_name "service1" + @service_resource.service_description "service1 Service" + + @endpoint_resource = Chef::Resource::OpenstackIdentityRegister.new("endpoint1", @run_context) + @endpoint_resource.endpoint_region "Region One" + @endpoint_resource.service_type "compute" + @endpoint_resource.endpoint_publicurl "http://public" + @endpoint_resource.endpoint_internalurl "http://internal" + @endpoint_resource.endpoint_adminurl "http://admin" + + @role_resource = Chef::Resource::OpenstackIdentityRegister.new("role1", @run_context) + @role_resource.role_name "role1" + + @user_resource = Chef::Resource::OpenstackIdentityRegister.new("user1", @run_context) + @user_resource.user_name "user1" + @user_resource.tenant_name "tenant1" + @user_resource.user_pass "password" + + @grant_resource = Chef::Resource::OpenstackIdentityRegister.new("grant1", @run_context) + @grant_resource.user_name "user1" + @grant_resource.tenant_name "tenant1" + @grant_resource.role_name "role1" + + @ec2_resource = Chef::Resource::OpenstackIdentityRegister.new("ec2", @run_context) + @ec2_resource.user_name "user1" + @ec2_resource.tenant_name "tenant1" + end + + it "should create a tenant" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@tenant_resource, @run_context) + provider.stub!(:identity_uuid).with(@tenant_resource, "tenant", "name", "tenant1") + provider.stub!(:identity_command).with(@tenant_resource, "tenant-create", + {"name" => "tenant1", "description" => "tenant1 Tenant", "enabled" => true}) + provider.run_action(:create_tenant) + @tenant_resource.should be_updated + end + it "should not create a new tenant if already exists" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@tenant_resource, @run_context) + provider.stub!(:identity_uuid).with(@tenant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.run_action(:create_tenant) + @tenant_resource.should_not be_updated + end + it "should create a service" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, @run_context) + provider.stub!(:identity_uuid).with(@service_resource, "service", "type", "compute") + provider.stub!(:identity_command).with(@service_resource, "service-create", + {"type" => "compute", "name" => "service1", "description" => "service1 Service"}) + provider.run_action(:create_service) + @service_resource.should be_updated + end + it "should not create a service if already exists" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, @run_context) + provider.stub!(:identity_uuid).with(@service_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") + provider.run_action(:create_service) + @service_resource.should_not be_updated + end + it "should not create a service if using a templated backend" do + node = Chef::Node.new + node.set["openstack"] = {"identity" => {"catalog" => { "backend" => "templated" }} } + cookbook_collection = Chef::CookbookCollection.new([]) + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, cookbook_collection, events) + provider = Chef::Provider::OpenstackIdentityRegister.new(@service_resource, run_context) + provider.run_action(:create_service) + @service_resource.should_not be_updated + end + it "should create an endpoint" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, @run_context) + provider.stub!(:identity_uuid).with(@endpoint_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@endpoint_resource, "endpoint", "service_id", "1234567890ABCDEFGH") + provider.stub!(:identity_command).with(@endpoint_resource, "endpoint-create", { + "region" => "Region One", "service_id" => "1234567890ABCDEFGH", "publicurl" => "http://public", + "internalurl" => "http://internal", "adminurl" => "http://admin"}) + provider.run_action(:create_endpoint) + @endpoint_resource.should be_updated + end + it "should not create a endpoint if already exists" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, @run_context) + provider.stub!(:identity_uuid).with(@endpoint_resource, "service", "type", "compute").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@endpoint_resource, "endpoint", "service_id", "1234567890ABCDEFGH").and_return("0987654321HGFEDCBA") + provider.run_action(:create_endpoint) + @endpoint_resource.should_not be_updated + end + it "should not create an endpoint if using a templated backend" do + node = Chef::Node.new + node.set["openstack"] = {"identity" => {"catalog" => { "backend" => "templated" }} } + cookbook_collection = Chef::CookbookCollection.new([]) + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, cookbook_collection, events) + provider = Chef::Provider::OpenstackIdentityRegister.new(@endpoint_resource, run_context) + provider.run_action(:create_endpoint) + @endpoint_resource.should_not be_updated + end + it "should create a role" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@role_resource, @run_context) + provider.stub!(:identity_uuid).with(@role_resource, "role", "name", "role1") + provider.stub!(:identity_command).with(@role_resource, "role-create", {"name" => "role1"}) + provider.run_action(:create_role) + @role_resource.should be_updated + end + it "should not create a role if already exists" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@role_resource, @run_context) + provider.stub!(:identity_uuid).with(@role_resource, "role", "name", "role1").and_return("1234567890ABCDEFGH") + provider.run_action(:create_role) + @role_resource.should_not be_updated + end + it "should create a user" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@user_resource, @run_context) + provider.stub!(:identity_uuid).with(@user_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_command).with(@user_resource, "user-list", {"tenant-id" => "1234567890ABCDEFGH"}) + provider.stub!(:identity_command).with(@user_resource, "user-create", + {"name" => "user1", "tenant-id" => "1234567890ABCDEFGH", "pass" => "password", "enabled" => true}) + provider.stub!(:prettytable_to_array).and_return([]) + provider.run_action(:create_user) + @user_resource.should be_updated + end + it "should not create a user if already exists" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@user_resource, @run_context) + provider.stub!(:identity_uuid).with(@user_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_command).with(@user_resource, "user-list", {"tenant-id" => "1234567890ABCDEFGH"}) + provider.stub!(:prettytable_to_array).and_return([{"name" => "user1"}]) + provider.stub!(:identity_uuid).with(@user_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") + provider.run_action(:create_user) + @user_resource.should_not be_updated + end + it "should grant a role" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@grant_resource, @run_context) + provider.stub!(:identity_uuid).with(@grant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@grant_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") + provider.stub!(:identity_uuid).with(@grant_resource, "role", "name", "role1").and_return("ABC1234567890DEF") + provider.stub!(:identity_uuid).with(@grant_resource, "user-role", "name", "role1", + { "tenant-id" => "1234567890ABCDEFGH", "user-id" => "HGFEDCBA0987654321" }).and_return("ABCD1234567890EFGH") + provider.stub!(:identity_command).with(@grant_resource, "user-role-add", + {"tenant-id" => "1234567890ABCDEFGH", "role-id" => "ABC1234567890DEF", "user-id" => "HGFEDCBA0987654321"}) + provider.run_action(:grant_role) + @grant_resource.should be_updated + end + it "should not grant a role if already granted" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@grant_resource, @run_context) + provider.stub!(:identity_uuid).with(@grant_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@grant_resource, "user", "name", "user1").and_return("HGFEDCBA0987654321") + provider.stub!(:identity_uuid).with(@grant_resource, "role", "name", "role1").and_return("ABC1234567890DEF") + provider.stub!(:identity_uuid).with(@grant_resource, "user-role", "name", "role1", + {"tenant-id" => "1234567890ABCDEFGH", "user-id" => "HGFEDCBA0987654321" }).and_return("ABC1234567890DEF") + provider.stub!(:identity_command).with(@grant_resource, "user-role-add", + {"tenant-id" => "1234567890ABCDEFGH", "role-id" => "ABC1234567890DEF", "user-id" => "HGFEDCBA0987654321"}) + provider.run_action(:grant_role) + @grant_resource.should_not be_updated + end + it "should grant ec2 creds" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@ec2_resource, @run_context) + provider.stub!(:identity_uuid).with(@ec2_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@ec2_resource, "user", "name", "user1", + {"tenant-id" => "1234567890ABCDEFGH"}).and_return("HGFEDCBA0987654321") + provider.stub!(:identity_uuid).with(@ec2_resource, "ec2-credentials", "tenant", "tenant1", + {"user-id" => "HGFEDCBA0987654321"}, "access") + provider.stub!(:identity_command).with(@ec2_resource, "ec2-credentials-create", + {"user-id" => "HGFEDCBA0987654321", "tenant-id" => "1234567890ABCDEFGH"}) + provider.stub!(:prettytable_to_array).and_return([{"access" => "access", "secret" => "secret"}]) + provider.run_action(:create_ec2_credentials) + @ec2_resource.should be_updated + end + it "should grant ec2 creds if they already exist" do + provider = Chef::Provider::OpenstackIdentityRegister.new(@ec2_resource, @run_context) + provider.stub!(:identity_uuid).with(@ec2_resource, "tenant", "name", "tenant1").and_return("1234567890ABCDEFGH") + provider.stub!(:identity_uuid).with(@ec2_resource, "user", "name", "user1", + {"tenant-id" => "1234567890ABCDEFGH"}).and_return("HGFEDCBA0987654321") + provider.stub!(:identity_uuid).with(@ec2_resource, "ec2-credentials", "tenant", "tenant1", + {"user-id" => "HGFEDCBA0987654321"}, "access").and_return("ABC1234567890DEF") + provider.run_action(:create_ec2_credentials) + @ec2_resource.should_not be_updated + end + + describe "#identity_command" do + it "should handle false values and long descriptions" do + provider = Chef::Provider::OpenstackIdentityRegister.new( + @user_resource, @run_context) + + provider.stub!(:shell_out).with( + ["keystone", "user-create", "--enabled", "false", + "--description", "more than one word"], + {:env => {"OS_SERVICE_ENDPOINT" => nil, "OS_SERVICE_TOKEN" => nil}} + ).and_return double("shell_out", :exitstatus => 0, :stdout => "good") + + provider.send( + :identity_command, @user_resource, "user-create", + {"enabled" => false, "description" => "more than one word"} + ).should eq "good" + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb new file mode 100644 index 0000000..77e18ea --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/server-opensuse_spec.rb @@ -0,0 +1,115 @@ +require_relative "spec_helper" + +describe "openstack-identity::server" do + before { identity_stubs } + describe "suse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-identity::server" + end + + it "installs mysql python packages" do + expect(@chef_run).to install_package "python-mysql" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" + end + chef_run.converge "openstack-identity::server" + + expect(chef_run).to install_package "python-psycopg2" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-python-memcached" + end + + it "installs keystone packages" do + expect(@chef_run).to upgrade_package "openstack-keystone" + end + + it "starts keystone on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-keystone" + end + + describe "/etc/keystone" do + before do + @dir = @chef_run.directory "/etc/keystone" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "openstack-keystone", "openstack-keystone" + end + end + + describe "/etc/keystone/ssl" do + before do + opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["auth"]["strategy"] = "pki" + end + chef_run.converge "openstack-identity::server" + @dir = chef_run.directory "/etc/keystone/ssl" + end + + it "has proper owner" do + expect(@dir). + to be_owned_by "openstack-keystone", "openstack-keystone" + end + end + + it "deletes keystone.db" do + expect(@chef_run).to delete_file "/var/lib/keystone/keystone.db" + end + + it "runs pki setup" do + opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["auth"]["strategy"] = "pki" + end + chef_run.converge "openstack-identity::server" + cmd = "keystone-manage pki_setup" + + expect(chef_run).to execute_command(cmd).with( + :user => "openstack-keystone" + ) + end + + describe "keystone.conf" do + before do + @template = @chef_run.template "/etc/keystone/keystone.conf" + end + + it "has proper owner" do + expect(@template). + to be_owned_by "openstack-keystone", "openstack-keystone" + end + + it "template contents" do + pending "TODO: implement" + end + end + + describe "default_catalog.templates" do + before do + opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["identity"]["catalog"]["backend"] = "templated" + end + chef_run.converge "openstack-identity::server" + @template = chef_run. + template "/etc/keystone/default_catalog.templates" + end + + it "has proper owner" do + expect(@template). + to be_owned_by "openstack-keystone", "openstack-keystone" + end + + it "template contents" do + pending "TODO: implement" + end + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb new file mode 100644 index 0000000..7061d33 --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/server-redhat_spec.rb @@ -0,0 +1,36 @@ +require_relative "spec_helper" + +describe "openstack-identity::server" do + before { identity_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-identity::server" + end + + it "installs mysql python packages" do + expect(@chef_run).to install_package "MySQL-python" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| + n.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" + end + chef_run.converge "openstack-identity::server" + + expect(chef_run).to install_package "python-psycopg2" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-memcached" + end + + it "installs keystone packages" do + expect(@chef_run).to upgrade_package "openstack-keystone" + end + + it "starts keystone on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-keystone" + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/server_spec.rb b/chef/cookbooks/openstack-identity/spec/server_spec.rb new file mode 100644 index 0000000..1e2f75b --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/server_spec.rb @@ -0,0 +1,260 @@ +require_relative "spec_helper" + +describe "openstack-identity::server" do + before { identity_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["identity"]["syslog"]["use"] = true + n.set["openstack"]["endpoints"]["identity-api"] = { + "host" => "127.0.1.1", + "port" => "5000", + "scheme" => "https" + } + n.set["openstack"]["endpoints"]["identity-admin"] = { + "host" => "127.0.1.1", + "port" => "35357", + "scheme" => "https" + } + end + @chef_run.converge "openstack-identity::server" + end + + it "runs logging recipe if node attributes say to" do + expect(@chef_run).to include_recipe "openstack-common::logging" + end + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-identity::server" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + it "installs mysql python packages" do + expect(@chef_run).to install_package "python-mysqldb" + end + + it "installs postgresql python packages if explicitly told" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + node = chef_run.node + node.set["openstack"]["db"]["identity"]["db_type"] = "postgresql" + chef_run.converge "openstack-identity::server" + + expect(chef_run).to install_package "python-psycopg2" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-memcache" + end + + it "installs keystone packages" do + expect(@chef_run).to upgrade_package "keystone" + end + + it "starts keystone on boot" do + expect(@chef_run).to set_service_to_start_on_boot "keystone" + end + + it "sleep on keystone service enable" do + expect(@chef_run.service("keystone")). + to notify "execute[Keystone: sleep]", :run + end + + describe "/etc/keystone" do + before do + @dir = @chef_run.directory "/etc/keystone" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "keystone", "keystone" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + describe "/etc/keystone/ssl" do + before { @dir = "/etc/keystone/ssl" } + + describe "without pki" do + it "doesn't create" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-identity::server" + + expect(chef_run).not_to create_directory @dir + end + end + + describe "with pki" do + before do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + @chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["auth"]["strategy"] = "pki" + end + @chef_run.converge "openstack-identity::server" + @directory = @chef_run.directory @dir + end + + it "creates" do + expect(@chef_run).to create_directory @directory.name + end + + it "has proper owner" do + expect(@directory).to be_owned_by "keystone", "keystone" + end + + it "has proper modes" do + expect(sprintf("%o", @directory.mode)).to eq "700" + end + end + end + + it "deletes keystone.db" do + expect(@chef_run).to delete_file "/var/lib/keystone/keystone.db" + end + + describe "pki setup" do + before { @cmd = "keystone-manage pki_setup" } + + describe "without pki" do + it "doesn't execute" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + + expect(chef_run).not_to execute_command(@cmd).with( + :user => "keystone" + ) + end + end + + describe "with pki" do + before do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + @chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["auth"]["strategy"] = "pki" + end + end + + it "executes" do + ::FileTest.should_receive(:exists?). + with("/etc/keystone/ssl/private/signing_key.pem"). + and_return(false) + @chef_run.converge "openstack-identity::server" + + expect(@chef_run).to execute_command(@cmd).with( + :user => "keystone" + ) + end + + it "doesn't execute when dir exists" do + ::FileTest.should_receive(:exists?). + with("/etc/keystone/ssl/private/signing_key.pem"). + and_return(true) + @chef_run.converge "openstack-identity::server" + + expect(@chef_run).not_to execute_command(@cmd).with( + :user => "keystone" + ) + end + end + end + + describe "keystone.conf" do + before do + @template = @chef_run.template "/etc/keystone/keystone.conf" + end + + it "has proper owner" do + expect(@template).to be_owned_by "keystone", "keystone" + end + + it "has proper modes" do + expect(sprintf("%o", @template.mode)).to eq "644" + end + + it "has bind host" do + expect(@chef_run).to create_file_with_content @template.name, + "bind_host = 127.0.1.1" + end + + it "has proper public and admin endpoint" do + expect(@chef_run).to create_file_with_content @template.name, + "public_endpoint = https://127.0.1.1:5000/" + expect(@chef_run).to create_file_with_content @template.name, + "admin_endpoint = https://127.0.1.1:35357/" + end + + it "notifies keystone restart" do + expect(@template).to notify "service[keystone]", :restart + end + end + + describe "default_catalog.templates" do + before { @file = "/etc/keystone/default_catalog.templates" } + + describe "without templated" do + it "doesn't create" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-identity::server" + + expect(chef_run).not_to create_file @file + end + end + + describe "with templated" do + before do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + @chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["openstack"]["identity"]["catalog"]["backend"] = "templated" + end + @chef_run.converge "openstack-identity::server" + @template = @chef_run.template @file + end + + it "creates" do + expect(@chef_run).to create_file @file + end + + it "has proper owner" do + expect(@template).to be_owned_by "keystone", "keystone" + end + + it "has proper modes" do + expect(sprintf("%o", @template.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies keystone restart" do + expect(@template).to notify "service[keystone]", :restart + end + end + end + + describe "db_sync" do + before do + @cmd = "keystone-manage db_sync" + end + + it "runs migrations" do + expect(@chef_run).to execute_command @cmd + end + + it "doesn't run migrations" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| + n.set["openstack"]["identity"]["db"]["migrate"] = false + end + chef_run.converge "openstack-identity::server" + + expect(chef_run).not_to execute_command @cmd + end + end + end +end diff --git a/chef/cookbooks/openstack-identity/spec/spec_helper.rb b/chef/cookbooks/openstack-identity/spec/spec_helper.rb new file mode 100644 index 0000000..b90096a --- /dev/null +++ b/chef/cookbooks/openstack-identity/spec/spec_helper.rb @@ -0,0 +1,27 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def identity_stubs + ::Chef::Recipe.any_instance.stub(:address_for). + with("lo"). + and_return "127.0.1.1" + ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] + ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:secret).and_return String.new +end diff --git a/chef/cookbooks/openstack-identity/templates/default/default_catalog.templates.erb b/chef/cookbooks/openstack-identity/templates/default/default_catalog.templates.erb new file mode 100644 index 0000000..580b1a1 --- /dev/null +++ b/chef/cookbooks/openstack-identity/templates/default/default_catalog.templates.erb @@ -0,0 +1,29 @@ +catalog.<%= node["openstack"]["identity"]["region"] %>.identity.publicURL = <%= @uris["identity"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.identity.adminURL = <%= @uris["identity-admin"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.identity.internalURL = <%= @uris["identity"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.identity.name = Identity Service + +catalog.<%= node["openstack"]["identity"]["region"] %>.compute.publicURL = <%= @uris["compute"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.compute.adminURL = <%= @uris["compute"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.compute.internalURL = <%= @uris["compute"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.compute.name = Compute Service + +catalog.<%= node["openstack"]["identity"]["region"] %>.volume.publicURL = <%= @uris["volume"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.volume.adminURL = <%= @uris["volume"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.volume.internalURL = <%= @uris["volume"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.volume.name = Volume Service + +catalog.<%= node["openstack"]["identity"]["region"] %>.ec2.publicURL = <%= @uris["ec2"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.ec2.adminURL = <%= @uris["ec2"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.ec2.internalURL = <%= @uris["ec2"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.ec2.name = EC2 Service + +catalog.<%= node["openstack"]["identity"]["region"] %>.image.publicURL = <%= @uris["image"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.image.adminURL = <%= @uris["image"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.image.internalURL = <%= @uris["image"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.image.name = Image Service + +catalog.<%= node["openstack"]["identity"]["region"] %>.network.publicURL = <%= @uris["network"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.network.adminURL = <%= @uris["network"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.network.internalURL = <%= @uris["network"].to_s %> +catalog.<%= node["openstack"]["identity"]["region"] %>.network.name = Network Service diff --git a/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb b/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb new file mode 100644 index 0000000..236fa00 --- /dev/null +++ b/chef/cookbooks/openstack-identity/templates/default/keystone.conf.erb @@ -0,0 +1,173 @@ +<%= node["openstack"]["identity"]["custom_template_banner"] %> + +[DEFAULT] +public_port = <%= node["openstack"]["identity"]["service_port"] %> +admin_port = <%= node["openstack"]["identity"]["admin_port"] %> +admin_token = <%= @bootstrap_token %> +#bind_host = <%= @ip_address %> +bind_host = <%= node['openstack']['endpoints']['identity-api']['host'] %> +compute_port = 8774 +verbose = <%= node["openstack"]["identity"]["verbose"] %> +debug = <%= node["openstack"]["identity"]["debug"] %> +<% if node["openstack"]["identity"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% else %> +log_file = /var/log/keystone/keystone.log +<% end %> +public_endpoint = <%= @public_endpoint %> +admin_endpoint = <%= @admin_endpoint %> + +<% if @memcache_servers -%> +[memcache] +servers = <%= @memcache_servers %> + +<% end -%> +[sql] +connection = <%= @sql_connection %> +idle_timeout = 200 +min_pool_size = 5 +max_pool_size = 10 +pool_timeout = 200 + +[ldap] +#url = ldap://localhost +#tree_dn = dc=example,dc=com +#user_tree_dn = ou=Users,dc=example,dc=com +#role_tree_dn = ou=Roles,dc=example,dc=com +#tenant_tree_dn = ou=Groups,dc=example,dc=com +#user = dc=Manager,dc=example,dc=com +#password = freeipa4all +#suffix = cn=example,cn=com + +[identity] +driver = keystone.identity.backends.<%= node["openstack"]["identity"]["identity"]["backend"] %>.Identity + +[catalog] +<% if node["openstack"]["identity"]["catalog"]["backend"] == "templated" -%> +# templated driver uses different class name :( +driver = keystone.catalog.backends.templated.TemplatedCatalog +<% else -%> +driver = keystone.catalog.backends.<%= node["openstack"]["identity"]["catalog"]["backend"] %>.Catalog +<% end -%> +template_file = /etc/keystone/default_catalog.templates + +[token] +driver = keystone.token.backends.<%= node["openstack"]["identity"]["token"]["backend"] %>.Token + +# Amount of time a token should remain valid (in seconds) +expiration = 86400 + +[policy] +driver = keystone.policy.backends.rules.Policy + +[ec2] +driver = keystone.contrib.ec2.backends.sql.Ec2 + +[ssl] +#enable = True +#certfile = /etc/keystone/ssl/certs/keystone.pem +#keyfile = /etc/keystone/ssl/private/keystonekey.pem +#ca_certs = /etc/keystone/ssl/certs/ca.pem +#cert_required = True + +[signing] +<% if node["openstack"]["auth"]["strategy"] == "pki" -%> +token_format = PKI +certfile = <%= node["openstack"]["identity"]["signing"]["certfile"] %> +keyfile = <%= node["openstack"]["identity"]["signing"]["keyfile"] %> +ca_certs = <%= node["openstack"]["identity"]["signing"]["ca_certs"] %> +key_size = <%= node["openstack"]["identity"]["signing"]["key_size"] %> +valid_days = <%= node["openstack"]["identity"]["signing"]["valid_days"] %> +ca_password = <%= node["openstack"]["identity"]["signing"]["ca_password"] %> +<% else -%> +token_format = UUID +<% end -%> + +[auth] +methods = password,token +password = keystone.auth.plugins.password.Password +token = keystone.auth.plugins.token.Token + +[filter:debug] +paste.filter_factory = keystone.common.wsgi:Debug.factory + +[filter:token_auth] +paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory + +[filter:admin_token_auth] +paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory + +[filter:xml_body] +paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory + +[filter:json_body] +paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory + +[filter:user_crud_extension] +paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory + +[filter:crud_extension] +paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory + +[filter:ec2_extension] +paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory + +[filter:s3_extension] +paste.filter_factory = keystone.contrib.s3:S3Extension.factory + +[filter:url_normalize] +paste.filter_factory = keystone.middleware:NormalizingFilter.factory + +[filter:sizelimit] +paste.filter_factory = keystone.middleware:RequestBodySizeLimiter.factory + +[filter:stats_monitoring] +paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory + +[filter:stats_reporting] +paste.filter_factory = keystone.contrib.stats:StatsExtension.factory + +[filter:access_log] +paste.filter_factory = keystone.contrib.access:AccessLogMiddleware.factory + +[app:public_service] +paste.app_factory = keystone.service:public_app_factory + +[app:service_v3] +paste.app_factory = keystone.service:v3_app_factory + +[app:admin_service] +paste.app_factory = keystone.service:admin_app_factory + +[pipeline:public_api] +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service + +[pipeline:admin_api] +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service + +[pipeline:api_v3] +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension service_v3 + +[app:public_version_service] +paste.app_factory = keystone.service:public_version_app_factory + +[app:admin_version_service] +paste.app_factory = keystone.service:admin_version_app_factory + +[pipeline:public_version_api] +pipeline = access_log sizelimit stats_monitoring url_normalize xml_body public_version_service + +[pipeline:admin_version_api] +pipeline = access_log sizelimit stats_monitoring url_normalize xml_body admin_version_service + +[composite:main] +use = egg:Paste#urlmap +/v2.0 = public_api +/v3 = api_v3 +/ = public_version_api + +[composite:admin] +use = egg:Paste#urlmap +/v2.0 = admin_api +/v3 = api_v3 +/ = admin_version_api diff --git a/chef/cookbooks/openstack-image/.tailor b/chef/cookbooks/openstack-image/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-image/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-image/Berksfile b/chef/cookbooks/openstack-image/Berksfile new file mode 100644 index 0000000..99c7145 --- /dev/null +++ b/chef/cookbooks/openstack-image/Berksfile @@ -0,0 +1,6 @@ +metadata + +cookbook "openstack-identity", + git: "git://github.com/stackforge/cookbook-openstack-identity.git" +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-image/Berksfile.lock b/chef/cookbooks/openstack-image/Berksfile.lock new file mode 100644 index 0000000..9b12c3c --- /dev/null +++ b/chef/cookbooks/openstack-image/Berksfile.lock @@ -0,0 +1,41 @@ +{ + "sources": { + "openstack-image": { + "path": "." + }, + "openstack-identity": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-identity.git", + "ref": "b881af26095cfa869a6970067c49597a0ee63586" + }, + "openstack-common": { + "locked_version": "0.4.2", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "6354e0280ac91b86f244923287380d66ff2f06c6" + }, + "apt": { + "locked_version": "2.0.0" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-image/Gemfile b/chef/cookbooks/openstack-image/Gemfile new file mode 100644 index 0000000..04ef97e --- /dev/null +++ b/chef/cookbooks/openstack-image/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-image/Gemfile.lock b/chef/cookbooks/openstack-image/Gemfile.lock new file mode 100644 index 0000000..dcf7d6c --- /dev/null +++ b/chef/cookbooks/openstack-image/Gemfile.lock @@ -0,0 +1,214 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.8) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.9.0) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.8) + multipart-post (~> 1.2.0) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.2.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.4) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.2.0) + multi_json (1.7.7) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.9) + net-ssh (2.6.8) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.18.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.5) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.4) + rspec-expectations (2.14.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.2) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.8.0) + strainer (3.1.1) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.3) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.1.1) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-image/README.md b/chef/cookbooks/openstack-image/README.md new file mode 100644 index 0000000..92b7c23 --- /dev/null +++ b/chef/cookbooks/openstack-image/README.md @@ -0,0 +1,166 @@ +Description +=========== + +This cookbook installs the OpenStack Image service **Glance** as part of an OpenStack reference deployment Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Glance is installed from packages, optionally populating the repository with default images. + +http://glance.openstack.org/ + +Requirements +============ + +Chef 0.10.0 or higher required (for Chef environment use). + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* openstack-common +* openstack-identity + +Usage +===== + +api +------ +- Installs the image-api server + +registry +-------- +- Installs the image-registry server + +keystone-registration +--------------------- +- Registers the API endpoint and glance service Keystone user + +The Glance cookbook currently supports file, swift, and Rackspace Cloud Files (swift API compliant) backing stores. NOTE: changing the storage location from cloudfiles to swift (and vice versa) requires that you manually export and import your stored images. + +To enable these features set the following in the default attributes section in your environment: + +Files +----- + +```json +"openstack": { + "image": { + "api": { + "default_store": "file" + }, + "upload_images": [ + "cirros" + ], + "image_upload": true + } +} +``` + +Swift +----- + +```json +"openstack": { + "image": { + "api": { + "default_store": "swift" + }, + "upload_images": [ + "cirros" + ], + "image_upload": true + } +} +``` + +Providers +========= + +image +----- + +Action: `:upload` + +- `:image_url`: Location of the image to be loaded into Glance. +- `:image_name`: A name for the image. +- `:image_type`: `qcow2` or `ami`. Defaults to `qcow2`. +- `:identity_user`: Username of the Keystone admin user. +- `:identity_pass`: Password for the Keystone admin user. +- `:identity_tenant`: Name of the Keystone admin user's tenant. +- `:identity_uri`: URI of the Identity API endpoint. + +Attributes +========== + +Attributes for the Image service are in the ['openstack']['image'] namespace. + +* `openstack['image']['verbose']` - Enables/disables verbose output for glance services. +* `openstack['image']['debug']` - Enables/disables debug output for glance services. +* `openstack['image']['identity_service_chef_role']` - The name of the Chef role that installs the Keystone Service API +* `openstack['image']['user'] - User glance runs as +* `openstack['image']['group'] - Group glance runs as +* `openstack['image']['db']['username']` - Username for glance database access +* `openstack['image']['api']['adminURL']` - Used when registering image endpoint with keystone +* `openstack['image']['api']['internalURL']` - Used when registering image endpoint with keystone +* `openstack['image']['api']['publicURL']` - Used when registering image endpoint with keystone +* `openstack['image']['service_tenant_name']` - Tenant name used by glance when interacting with keystone - used in the API and registry paste.ini files +* `openstack['image']['service_user']` - User name used by glance when interacting with keystone - used in the API and registry paste.ini files +* `openstack['image']['service_role']` - User role used by glance when interacting with keystone - used in the API and registry paste.ini files +* `openstack['image']['api']['auth']['cache_dir']` - Defaults to `/var/cache/glance/api`. Directory where `auth_token` middleware writes certificates for glance-api +* `openstack['image']['registry']['auth']['cache_dir']` - Defaults to `/var/cache/glance/registry`. Directory where `auth_token` middleware writes certificates for glance-registry +* `openstack['image']['image_upload']` - Toggles whether to automatically upload images in the `openstack['image']['upload_images']` array +* `openstack['image']['upload_images']` - Default list of images to upload to the glance repository as part of the install +* `openstack['image']['upload_image']['']` - URL location of the `` image. There can be multiple instances of this line to define multiple imagess (eg natty, maverick, fedora17 etc) +--- example `openstack['image']['upload_image']['natty']` - "http://c250663.r63.cf1.rackcdn.com/ubuntu-11.04-server-uec-amd64-multinic.tar.gz" +* `openstack['image']['api']['default_store']` - Toggles the backend storage type. Currently supported is "file" and "swift" +* `openstack['image']['api']['swift']['store_container']` - Set the container used by glance to store images and snapshots. Defaults to "glance" +* `openstack['image']['api']['swift']['store_large_object_size']` - Set the size at which glance starts to chunnk files. Defaults to "200" MB +* `openstack['image']['api']['swift']['store_large_object_chunk_size']` - Set the chunk size for glance. Defaults to "200" MB +* `openstack['image']['api']['rbd']['rbd_store_ceph_conf']` - Default location of ceph.conf +* `openstack['image']['api']['rbd']['rbd_store_user']` - User for connecting to ceph store +* `openstack['image']['api']['rbd']['rbd_store_pool']` - RADOS pool for images +* `openstack['image']['api']['rbd']['rbd_store_chunk_size']` - Size in MB of chunks for RADOS Store, should be a power of 2 + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +Author:: Justin Shepherd () +Author:: Jason Cannavale () +Author:: Ron Pedde () +Author:: Joseph Breu () +Author:: William Kelly () +Author:: Darren Birkett () +Author:: Evan Callicoat () +Author:: Matt Ray () +Author:: Jay Pipes () +Author:: John Dewey () +Author:: Craig Tracey () +Author:: Sean Gallagher () + +Copyright 2012, Rackspace US, Inc. +Copyright 2012-2013, Opscode, Inc. +Copyright 2012-2013, AT&T Services, Inc. +Copyright 2013, Craig Tracey + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-image/Strainerfile b/chef/cookbooks/openstack-image/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-image/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-image/attributes/default.rb b/chef/cookbooks/openstack-image/attributes/default.rb new file mode 100644 index 0000000..a8cabcf --- /dev/null +++ b/chef/cookbooks/openstack-image/attributes/default.rb @@ -0,0 +1,145 @@ +# +# Cookbook Name:: openstack-image +# Attributes:: default +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["image"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +default["openstack"]["image"]["verbose"] = "False" +default["openstack"]["image"]["debug"] = "False" +# This is the name of the Chef role that will install the Keystone Service API +default["openstack"]["image"]["identity_service_chef_role"] = "os-identity" + +# Gets set in the Image Endpoint when registering with Keystone +default["openstack"]["image"]["region"] = "RegionOne" + +# The name of the Chef role that knows about the message queue server +# that Glance uses +default["openstack"]["image"]["rabbit_server_chef_role"] = "os-ops-messaging" + +default["openstack"]["image"]["db"]["username"] = "glance" +# Execute database migrations. There are cases where migrations should not be +# executed. For example when upgrading a zone, and the image database is +# replicated across many zones. +default["openstack"]["image"]["db"]["migrate"] = true + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +default["openstack"]["image"]["rabbit"]["username"] = "guest" +default["openstack"]["image"]["rabbit"]["vhost"] = "/" +default["openstack"]["image"]["rabbit"]["port"] = 5672 +default["openstack"]["image"]["rabbit"]["host"] = "127.0.0.1" + +default["openstack"]["image"]["service_tenant_name"] = "service" +default["openstack"]["image"]["service_user"] = "glance" +default["openstack"]["image"]["service_role"] = "admin" + +# Setting this to v2.0. See discussion on +# https://bugs.launchpad.net/openstack-chef/+bug/1207504 +default["openstack"]["image"]["api"]["auth"]["version"] = "v2.0" + +# Keystone PKI signing directories +# XXX keystoneclient wants these dirs to exist even if it doesn't use them +default["openstack"]["image"]["api"]["auth"]["cache_dir"] = "/var/cache/glance/api" +default["openstack"]["image"]["registry"]["auth"]["cache_dir"] = "/var/cache/glance/registry" + +default["openstack"]["image"]["api"]["default_store"] = "file" +# If set, glance API service will bind to the address on this interface, +# otherwise it will bind to the API endpoint's host. +default["openstack"]["image"]["api"]["bind_interface"] = nil +default["openstack"]["image"]["api"]["swift"]["container"] = "glance" +default["openstack"]["image"]["api"]["swift"]["large_object_size"] = "200" +default["openstack"]["image"]["api"]["swift"]["large_object_chunk_size"] = "200" +default["openstack"]["image"]["api"]["cache"]["image_cache_max_size"] = "10737418240" + +# Ceph Options +default["openstack"]["image"]["api"]["rbd"]["rbd_store_ceph_conf"] = "/etc/ceph/ceph.conf" +default["openstack"]["image"]["api"]["rbd"]["rbd_store_user"] = "glance" +default["openstack"]["image"]["api"]["rbd"]["rbd_store_pool"] = "images" +default["openstack"]["image"]["api"]["rbd"]["rbd_store_chunk_size"] = "8" + +# If set, glance registry service will bind to the address on this interface, +# otherwise it will bind to the API endpoint's host. +default["openstack"]["image"]["registry"]["bind_interface"] = nil + +# API to use for accessing data. Default value points to sqlalchemy +# package. +default["openstack"]["image"]["data_api"] = "glance.db.sqlalchemy.api" + +# Default Image Locations +default["openstack"]["image"]["image_upload"] = false +default["openstack"]["image"]["upload_images"] = [ "cirros" ] +default["openstack"]["image"]["upload_image"]["precise"] = "http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img" +default["openstack"]["image"]["upload_image"]["oneiric"] = "http://cloud-images.ubuntu.com/oneiric/current/oneiric-server-cloudimg-amd64-disk1.img" +default["openstack"]["image"]["upload_image"]["natty"] = "http://cloud-images.ubuntu.com/natty/current/natty-server-cloudimg-amd64-disk1.img" +default["openstack"]["image"]["upload_image"]["cirros"] = "https://launchpadlibrarian.net/83305348/cirros-0.3.0-x86_64-disk.img" +# more images available at https://github.com/rackerjoe/oz-image-build +default["openstack"]["image"]["upload_image"]["centos"] = "http://c250663.r63.cf1.rackcdn.com/centos60_x86_64.qcow2" + +# logging attribute +default["openstack"]["image"]["syslog"]["use"] = false +default["openstack"]["image"]["syslog"]["facility"] = "LOG_LOCAL2" +default["openstack"]["image"]["syslog"]["config_facility"] = "local2" + +# platform-specific settings +case platform +when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this + default["openstack"]["image"]["user"] = "glance" + default["openstack"]["image"]["group"] = "glance" + default["openstack"]["image"]["platform"] = { + "postgresql_python_packages" => [ "python-psycopg2" ], + "mysql_python_packages" => [ "MySQL-python" ], + "image_packages" => [ "openstack-glance", "openstack-swift", "cronie" ], + "image_api_service" => "openstack-glance-api", + "image_registry_service" => "openstack-glance-registry", + "image_api_process_name" => "glance-api", + "package_overrides" => "" + } +when "suse" + default["openstack"]["image"]["user"] = "openstack-glance" + default["openstack"]["image"]["group"] = "openstack-glance" + default["openstack"]["image"]["platform"] = { + "postgresql_python_packages" => [ "python-psycopg2" ], + "mysql_python_packages" => [ "python-mysql" ], + "image_packages" => [ "openstack-glance", "openstack-swift", "python-glanceclient" ], + "image_api_service" => "openstack-glance-api", + "image_registry_service" => "openstack-glance-registry", + "image_api_process_name" => "glance-api", + "package_overrides" => "" + } +when "ubuntu" + default["openstack"]["image"]["user"] = "glance" + default["openstack"]["image"]["group"] = "glance" + default["openstack"]["image"]["platform"] = { + "postgresql_python_packages" => [ "python-psycopg2" ], + "mysql_python_packages" => [ "python-mysqldb" ], + "image_packages" => [ "glance", "python-swift" ], + "image_api_service" => "glance-api", + "image_registry_service" => "glance-registry", + "image_registry_process_name" => "glance-registry", + "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-image/files/default/glance_plugin.py b/chef/cookbooks/openstack-image/files/default/glance_plugin.py new file mode 100644 index 0000000..214dc88 --- /dev/null +++ b/chef/cookbooks/openstack-image/files/default/glance_plugin.py @@ -0,0 +1,117 @@ +from glance.client import V1Client +from glance.common import exception + +import collectd + +global NAME, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, OS_AUTH_STRATEGY, VERBOSE_LOGGING + +NAME = "glance_plugin" +OS_USERNAME = "username" +OS_PASSWORD = "password" +OS_TENANT_NAME = "tenantname" +OS_AUTH_URL = "http://localhost:5000/v2.0" +OS_AUTH_STRATEGY = "keystone" +VERBOSE_LOGGING = False + +def get_stats(user, passwd, tenant, url, host=None): + creds = {"username": user, "password": passwd, "tenant": tenant,"auth_url": url, "strategy": OS_AUTH_STRATEGY} + client = V1Client(host,creds=creds) + try: + image_list = client.get_images_detailed() + except exception.NotAuthenticated: + msg = "Client credentials appear to be invalid" + raise exception.ClientConnectionError(msg) + else: + # TODO(shep): this needs to be rewritten more inline with the keystone|nova plugins + data = dict() + data["count"] = int(len(image_list)) + data["bytes"] = 0 + data["snapshot.count"] = 0 + data["snapshot.bytes"] = 0 + data["tenant"] = dict() + for image in image_list: + data["bytes"] += int(image["size"]) + if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot": + data["snapshot.count"] += 1 + data["snapshot.bytes"] += int(image["size"]) + uuid = str(image["owner"]) + if uuid in data["tenant"]: + data["tenant"][uuid]["count"] += 1 + data["tenant"][uuid]["bytes"] += int(image["size"]) + if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot": + data["tenant"][uuid]["snapshot.count"] += 1 + data["tenant"][uuid]["snapshot.bytes"] += int(image["size"]) + else: + data["tenant"][uuid] = dict() + data["tenant"][uuid]["count"] = 1 + data["tenant"][uuid]["bytes"] = int(image["size"]) + data["tenant"][uuid]["snapshot.count"] = 0 + data["tenant"][uuid]["snapshot.bytes"] = 0 + if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot": + data["tenant"][uuid]["snapshot.count"] += 1 + data["tenant"][uuid]["snapshot.bytes"] += int(image["size"]) + # debug + #for key in data.keys(): + # if key == "tenant": + # for uuid in data[key].keys(): + # for field in data[key][uuid]: + # print "glance.images.tenant.%s.%s : %i" % (uuid, field, data[key][uuid][field]) + # else: + # print "glance.images.%s : %i" % (key, data[key]) + ########## + return data + +def configure_callback(conf): + """Received configuration information""" + global OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL + for node in conf.children: + if node.key == "Username": + OS_USERNAME = node.values[0] + elif node.key == "Password": + OS_PASSWORD = node.values[0] + elif node.key == "TenantName": + OS_TENANT_NAME = node.values[0] + elif node.key == "AuthURL": + OS_AUTH_URL = node.values[0] + elif node.key == "Verbose": + VERBOSE_LOGGING = node.values[0] + else: + logger("warn", "Unknown config key: %s" % node.key) + +def read_callback(): + logger("verb", "read_callback") + info = get_stats(OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL) + + if not info: + logger("err", "No information received") + return + + for key in info.keys(): + if key == "tenant": + for uuid in info[key].keys(): + for field in info[key][uuid]: + logger('verb', 'Dispatching glance.images.tenant.%s.%s : %i' % (uuid, field, int(info[key][uuid][field]))) + path = 'glance.images.%s.%s' % (uuid, field) + val = collectd.Values(plugin=path) + val.type = 'gauge' + val.values = [int(info[key][uuid][field])] + val.dispatch() + else: + logger('verb', 'Dispatching %s : %i' % (key, int(info[key]))) + path = 'glance.images.%s' % (key) + val = collectd.Values(plugin=path) + val.type = 'gauge' + val.values = [int(info[key])] + val.dispatch() + +def logger(t, msg): + if t == 'err': + collectd.error('%s: %s' % (NAME, msg)) + if t == 'warn': + collectd.warning('%s: %s' % (NAME, msg)) + elif t == 'verb' and VERBOSE_LOGGING == True: + collectd.info('%s: %s' % (NAME, msg)) + +collectd.register_config(configure_callback) +collectd.warning("Initializing glance plugin") +collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-image/metadata.rb b/chef/cookbooks/openstack-image/metadata.rb new file mode 100644 index 0000000..c8f841b --- /dev/null +++ b/chef/cookbooks/openstack-image/metadata.rb @@ -0,0 +1,16 @@ +name "openstack-image" +maintainer "Opscode, Inc." +license "Apache 2.0" +description "Installs and configures the Glance Image Registry and Delivery Service" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.0" +recipe "openstack-image::api", "Installs packages required for a glance api server" +recipe "openstack-image::registry", "Installs packages required for a glance registry server" +recipe "openstack-image::identity_registration", "Registers Glance endpoints and service with Keystone" + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "openstack-common", "~> 0.4.0" +depends "openstack-identity", "~> 7.0.0" diff --git a/chef/cookbooks/openstack-image/providers/image.rb b/chef/cookbooks/openstack-image/providers/image.rb new file mode 100644 index 0000000..cecf9f7 --- /dev/null +++ b/chef/cookbooks/openstack-image/providers/image.rb @@ -0,0 +1,108 @@ +# +# Cookbook Name:: openstack-image +# Provider:: image +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :upload do + @user = new_resource.identity_user + @pass = new_resource.identity_pass + @tenant = new_resource.identity_tenant + @ks_uri = new_resource.identity_uri + + name = new_resource.image_name + url = new_resource.image_url + type = new_resource.image_type + if type == "unknown" + type = _determine_type(url) + end + _upload_image(type, name, url) + new_resource.updated_by_last_action(true) +end + +private +def _determine_type(url) + # Lets do our best to determine the type from the file extension + case ::File.extname(url) + when ".gz", ".tgz" + return "ami" + when ".qcow2", ".img" + return "qcow" + end +end + +private +def _upload_image(type, name, url) + case type + when 'ami' + _upload_ami(name, url) + when 'qcow' + _upload_qcow(name, url) + end +end + +private +def _upload_qcow(name, url) + glance_cmd = "glance --insecure -I #{@user} -K #{@pass} -T #{@tenant} -N #{@ks_uri}" + c_fmt = "--container-format bare" + d_fmt = "--disk-format qcow2" + + execute "Uploading QCOW2 image #{name}" do + cwd "/tmp" + command "#{glance_cmd} image-create --name #{name} --is-public true #{c_fmt} #{d_fmt} --location #{url}" + not_if "#{glance_cmd} image-list | grep #{name.to_s}" + end +end + +private +def _upload_ami(name, url) + glance_cmd = "glance --insecure -I #{@user} -K #{@pass} -T #{@tenant} -N #{@ks_uri}" + aki_fmt = "--container-format aki --disk-format aki" + ari_fmt = "--container-format ari --disk-format ari" + ami_fmt = "--container-format ami --disk-format ami" + + bash "Uploading AMI image #{name}" do + cwd "/tmp" + user "root" + code <<-EOH + set -x + mkdir -p images/#{name} + cd images/#{name} + + curl -L #{url} | tar -zx + image_name=$(basename #{url} .tar.gz) + + image_name=${image_name%-multinic} + + kernel_file=$(ls *vmlinuz-virtual | head -n1) + if [ ${#kernel_file} -eq 0 ]; then + kernel_file=$(ls *vmlinuz | head -n1) + fi + + ramdisk=$(ls *-initrd | head -n1) + if [ ${#ramdisk} -eq 0 ]; then + ramdisk=$(ls *-loader | head -n1) + fi + + kernel=$(ls *.img | head -n1) + + kid=$(#{glance_cmd} image-create --name "${image_name}-kernel" --is-public true #{aki_fmt} < ${kernel_file} | cut -d: -f2 | sed 's/ //') + rid=$(#{glance_cmd} image-create --name "${image_name}-initrd" --is-public true #{ari_fmt} < ${ramdisk} | cut -d: -f2 | sed 's/ //') + #{glance_cmd} image-create --name "#{name}" --is-public true #{ami_fmt} --property "kernel_id=$kid" --property "ramdisk_id=$rid" < ${kernel} + EOH + not_if "#{glance_cmd} image-list | grep #{name.to_s}" + end +end diff --git a/chef/cookbooks/openstack-image/recipes/api.rb b/chef/cookbooks/openstack-image/recipes/api.rb new file mode 100644 index 0000000..2435121 --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/api.rb @@ -0,0 +1,249 @@ +# +# Cookbook Name:: openstack-image +# Recipe:: api +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012-2013, Opscode, Inc. +# Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +if node["openstack"]["image"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options = node["openstack"]["image"]["platform"] + +package "python-keystone" do + action :install +end + +package "curl" do + action :install +end + +platform_options["image_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +service "image-api" do + service_name platform_options["image_api_service"] + supports :status => true, :restart => true + + action :enable +end + +directory "/etc/glance" do + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00700 +end + +directory ::File.dirname node["openstack"]["image"]["api"]["auth"]["cache_dir"] do + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00700 +end + +template "/etc/glance/policy.json" do + source "policy.json.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + + notifies :restart, "service[image-api]", :immediately +end + +glance = node["openstack"]["image"] + +identity_endpoint = endpoint "identity-api" +identity_admin_endpoint = endpoint "identity-admin" +service_pass = service_password node['openstack']['identity']['image']['password'] + +#TODO(jaypipes): Move this logic and stuff into the openstack-common +# library cookbook. +auth_uri = identity_endpoint.to_s +if node["openstack"]["image"]["api"]["auth"]["version"] != "v2.0" + # The auth_uri should contain /v2.0 in most cases, but if the + # auth_version is v3.0, we leave it off. This is only necessary + # for environments that need to support V3 non-default-domain + # tokens, which is really the only reason to set version to + # something other than v2.0 (the default) + auth_uri = auth_uri.gsub('/v2.0', '') +end + +#db_user = node["openstack"]["image"]["db"]["username"] +#db_pass = db_password "glance" +db_user = node["openstack"]["db"]["image"]["password"] +db_pass = db_password node["openstack"]["db"]["image"]["password"] + +sql_connection = db_uri("image", db_user, db_pass) + +registry_endpoint = endpoint "image-registry" +api_endpoint = endpoint "image-api" +service_pass = service_password node['openstack']['identity']['image']['password'] +service_tenant_name = node['openstack']['identity']['image']['tenant'] +service_user = node['openstack']['identity']['image']['username'] + +# Possible combinations of options here +# - default_store=file +# * no other options required +# - default_store=swift +# * if swift_store_auth_address is not defined +# - default to local swift +# * else if swift_store_auth_address is defined +# - get swift_store_auth_address, swift_store_user, swift_store_key, and +# swift_store_auth_version from the node attributes and use them to connect +# to the swift compatible API service running elsewhere - possibly +# Rackspace Cloud Files. +if glance["api"]["swift_store_auth_address"].nil? + swift_store_auth_address = auth_uri + swift_store_user="#{service_tenant_name}:#{service_user}" + swift_user_tenant = nil + swift_store_key = service_pass + swift_store_auth_version=2 +else + swift_store_auth_address=glance["api"]["swift_store_auth_address"] + swift_user_tenant = glance["api"]["swift_user_tenant"] + swift_store_user=glance["api"]["swift_store_user"] + swift_store_key = service_password swift_store_user + swift_store_auth_version=glance["api"]["swift_store_auth_version"] +end + +# Only use the glance image cacher if we aren't using file for our backing store. +if glance["api"]["default_store"]=="file" + glance_flavor="keystone" +else + glance_flavor="keystone+cachemanagement" +end + +if node["openstack"]["image"]["api"]["bind_interface"].nil? + bind_address = api_endpoint.host +else + bind_address = address_for node["openstack"]["image"]["api"]["bind_interface"] +end + +template "/etc/glance/glance-api.conf" do + source "glance-api.conf.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + variables( + :api_bind_address => bind_address, + :api_bind_port => api_endpoint.port, + :registry_ip_address => registry_endpoint.host, + :registry_port => registry_endpoint.port, + :sql_connection => sql_connection, + :glance_flavor => glance_flavor, + :auth_uri => auth_uri, + :identity_admin_endpoint => identity_admin_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass, + :swift_store_key => swift_store_key, + :swift_user_tenant => swift_user_tenant, + :swift_store_user => swift_store_user, + :swift_store_auth_address => swift_store_auth_address, + :swift_store_auth_version => swift_store_auth_version + ) + + notifies :restart, "service[image-api]", :immediately +end + +template "/etc/glance/glance-api-paste.ini" do + source "glance-api-paste.ini.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + + notifies :restart, "service[image-api]", :immediately +end + +template "/etc/glance/glance-cache.conf" do + source "glance-cache.conf.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + variables( + :registry_ip_address => registry_endpoint.host, + :registry_port => registry_endpoint.port + ) + + notifies :restart, "service[image-api]" +end + +#TODO(jaypipes) I don't think this even exists or at least isn't +# used, since the Glance cache middleware goes in the api-paste.ini... +template "/etc/glance/glance-cache-paste.ini" do + source "glance-cache-paste.ini.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + + notifies :restart, "service[image-api]" +end + +template "/etc/glance/glance-scrubber.conf" do + source "glance-scrubber.conf.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 + variables( + :registry_ip_address => registry_endpoint.host, + :registry_port => registry_endpoint.port + ) +end + +# Configure glance-cache-pruner to run every 30 minutes +cron "glance-cache-pruner" do + minute "*/30" + command "/usr/bin/glance-cache-pruner" +end + +# Configure glance-cache-cleaner to run at 00:01 everyday +cron "glance-cache-cleaner" do + minute "01" + hour "00" + command "/usr/bin/glance-cache-cleaner" +end + +template "/etc/glance/glance-scrubber-paste.ini" do + source "glance-scrubber-paste.ini.erb" + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00644 +end + +if node["openstack"]["image"]["image_upload"] + node["openstack"]["image"]["upload_images"].each do |img| + openstack_image_image "Image setup for #{img.to_s}" do + image_url node["openstack"]["image"]["upload_image"][img.to_sym] + image_name img + identity_user service_user + identity_pass service_pass + identity_tenant service_tenant_name + identity_uri auth_uri + action :upload + end + end +end diff --git a/chef/cookbooks/openstack-image/recipes/identity_registration.rb b/chef/cookbooks/openstack-image/recipes/identity_registration.rb new file mode 100644 index 0000000..7c5f992 --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/identity_registration.rb @@ -0,0 +1,99 @@ +# +# Cookbook Name:: openstack-image +# Recipe:: identity_registration +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" + +token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +auth_url = ::URI.decode identity_admin_endpoint.to_s + +registry_endpoint = endpoint "image-registry" +api_endpoint = endpoint "image-api" + +service_pass = service_password node['openstack']['identity']['image']['password'] +service_tenant_name = node["openstack"]["image"]["service_tenant_name"] +service_user = node["openstack"]["image"]["service_user"] +service_role = node["openstack"]["image"]["service_role"] +region = node["openstack"]["image"]["region"] + +# Register Image Service +openstack_identity_register "Register Image Service" do + auth_uri auth_url + bootstrap_token token + service_name "glance" + service_type "image" + service_description "Glance Image Service" + + action :create_service +end + +# Register Image Endpoint +openstack_identity_register "Register Image Endpoint" do + auth_uri auth_url + bootstrap_token token + service_type "image" + endpoint_region region + endpoint_adminurl api_endpoint.to_s + endpoint_internalurl api_endpoint.to_s + endpoint_publicurl api_endpoint.to_s + + action :create_endpoint +end + +# Register Service Tenant +openstack_identity_register "Register Service Tenant" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + tenant_description "Service Tenant" + tenant_enabled true # Not required as this is the default + + action :create_tenant +end + +# Register Service User +openstack_identity_register "Register #{service_user} User" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + # String until https://review.openstack.org/#/c/29498/ merged + user_enabled true + + action :create_user +end + +## Grant Admin role to Service User for Service Tenant ## +openstack_identity_register "Grant '#{service_role}' Role to #{service_user} User for #{service_tenant_name} Tenant" do + auth_uri auth_url + bootstrap_token token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end diff --git a/chef/cookbooks/openstack-image/recipes/registry.rb b/chef/cookbooks/openstack-image/recipes/registry.rb new file mode 100644 index 0000000..f043804 --- /dev/null +++ b/chef/cookbooks/openstack-image/recipes/registry.rb @@ -0,0 +1,152 @@ +# +# Cookbook Name:: openstack-image +# Recipe:: registry +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +if node["openstack"]["image"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options = node["openstack"]["image"]["platform"] + +package "python-keystone" do + action :install +end + +db_user = node['openstack']['db']['image']['username'] +db_pass = db_password node['openstack']['db']['image']['password'] + +sql_connection = db_uri("image", db_user, db_pass) + +identity_endpoint = endpoint "identity-admin" +registry_endpoint = endpoint "image-registry" +service_tenant_name = node['openstack']['identity']['image']['tenant'] +service_user = node['openstack']['identity']['image']['username'] +service_pass = service_password node['openstack']['identity']['image']['password'] + +package "curl" do + action :install +end + +db_type = node['openstack']['db']['identity']['db_type'] +platform_options["#{db_type}_python_packages"].each do |pkg| + package pkg do + action :install + end +end + +platform_options["image_packages"].each do |pkg| + package pkg do + action :upgrade + end +end + +directory ::File.dirname(node["openstack"]["image"]["registry"]["auth"]["cache_dir"]) do + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00700 +end + +service "image-registry" do + service_name platform_options["image_registry_service"] + supports :status => true, :restart => true + + action :enable +end + +# Having to manually version the database because of Ubuntu bug +# https://bugs.launchpad.net/ubuntu/+source/glance/+bug/981111 +execute "glance-manage version_control 0" do + not_if "glance-manage db_version" + only_if { platform?(%w{ubuntu debian}) } +end + +file "/var/lib/glance/glance.sqlite" do + action :delete +end + +directory "/etc/glance" do + owner node["openstack"]["image"]["user"] + group node["openstack"]["image"]["group"] + mode 00700 +end + +if node["openstack"]["image"]["registry"]["bind_interface"].nil? + bind_address = registry_endpoint.host +else + bind_address = address_for node["openstack"]["image"]["registry"]["bind_interface"] +end + +template "/etc/glance/glance-registry.conf" do + source "glance-registry.conf.erb" + owner "root" + group "root" + mode 00644 + variables( + :registry_bind_address => bind_address, + :registry_port => registry_endpoint.port, + :sql_connection => sql_connection, + "identity_endpoint" => identity_endpoint, + "service_pass" => service_pass, + "service_tenant_name" => service_tenant_name, + "service_user" => service_user + ) + + notifies :restart, "service[image-registry]", :immediately +end + +execute "glance-manage db_sync" do + only_if { node["openstack"]["image"]["db"]["migrate"] } +end + +template "/etc/glance/glance-registry-paste.ini" do + source "glance-registry-paste.ini.erb" + owner "root" + group "root" + mode 00644 + + notifies :restart, "service[image-registry]", :immediately +end + +execute "tinyimage" do + command "sh /tmp/tinyimage.sh" + action :nothing +end + +identity_endpoint = endpoint "identity-api" +auth_uri = ::URI.decode identity_endpoint.to_s + +template "/tmp/tinyimage.sh" do + source "tinyimage.sh.erb" + owner "root" + group "root" + mode 00755 + variables( + :os_username => node['openstack']['identity']['admin_user'], + :os_password => node['openstack']['identity']['admin_password'], + :os_tenant_name => node['openstack']['identity']['admin_tenant_name'], + :os_auth_url => auth_uri + ) + + notifies :run, "execute[tinyimage]", :delayed +end diff --git a/chef/cookbooks/openstack-image/resources/image.rb b/chef/cookbooks/openstack-image/resources/image.rb new file mode 100644 index 0000000..774d431 --- /dev/null +++ b/chef/cookbooks/openstack-image/resources/image.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: openstack-image +# Resource:: image +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :upload + +# In earlier versions of Chef the LWRP DSL doesn't support specifying +# a default action, so you need to drop into Ruby. +def initialize(*args) + super + @action = :upload +end + +attribute :image_url, :kind_of => String +attribute :image_type, :kind_of => String, :default => "unknown", :equal_to => ["unknown", "ami", "qcow"] +attribute :image_name, :kind_of => String, :default => "default" +attribute :identity_user, :kind_of => String +attribute :identity_pass, :kind_of => String +attribute :identity_tenant, :kind_of => String +attribute :identity_uri, :kind_of => String diff --git a/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb b/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb new file mode 100644 index 0000000..8656aaa --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/api-redhat_spec.rb @@ -0,0 +1,15 @@ +require_relative "spec_helper" + +describe "openstack-image::api" do + before { image_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-image::api" + end + + it "starts glance api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-glance-api" + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/api_spec.rb b/chef/cookbooks/openstack-image/spec/api_spec.rb new file mode 100644 index 0000000..9d1f5b8 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/api_spec.rb @@ -0,0 +1,237 @@ +require_relative "spec_helper" + +describe "openstack-image::api" do + before { image_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["image"]["syslog"]["use"] = true + n.set["cpu"] = { 'total' => '1' } + end + @chef_run.converge "openstack-image::api" + end + + expect_runs_openstack_common_logging_recipe + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-image::api" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + expect_installs_python_keystone + + expect_installs_curl + + expect_installs_ubuntu_glance_packages + + it "starts glance api on boot" do + expect(@chef_run).to set_service_to_start_on_boot "glance-api" + end + + expect_creates_glance_dir + + expect_creates_cache_dir + + describe "policy.json" do + before do + @file = @chef_run.template "/etc/glance/policy.json" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "notifies image-api restart" do + expect(@file).to notify "service[image-api]", :restart + end + end + + describe "glance-api.conf" do + before do + @file = @chef_run.template "/etc/glance/glance-api.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has bind host when bind_interface not specified" do + expect(@chef_run).to create_file_with_content @file.name, + "bind_host = 127.0.0.1" + end + + it "has bind host when bind_interface specified" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["image"]["api"]["bind_interface"] = "lo" + n.set["cpu"] = { 'total' => '1' } + end + chef_run.converge "openstack-image::api" + + expect(chef_run).to create_file_with_content @file.name, + "bind_host = 127.0.1.1" + end + + it "notifies image-api restart" do + expect(@file).to notify "service[image-api]", :restart + end + end + + describe "glance-api-paste.ini" do + before do + @file = @chef_run.template "/etc/glance/glance-api-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies image-api restart" do + expect(@file).to notify "service[image-api]", :restart + end + end + + describe "glance-cache.conf" do + before do + @file = @chef_run.template "/etc/glance/glance-cache.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies image-api restart" do + expect(@file).to notify "service[image-api]", :restart + end + end + + describe "glance-cache-paste.ini" do + before do + @file = @chef_run.template "/etc/glance/glance-cache-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies image-api restart" do + expect(@file).to notify "service[image-api]", :restart + end + end + + describe "glance-scrubber.conf" do + before do + @file = @chef_run.template "/etc/glance/glance-scrubber.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + it "has glance-cache-pruner cronjob running every 30 minutes" do + cron = @chef_run.cron "glance-cache-pruner" + + expect(cron.command).to eq "/usr/bin/glance-cache-pruner" + expect(cron.minute).to eq "*/30" + end + + it "has glance-cache-cleaner to run at 00:01 each day" do + cron = @chef_run.cron "glance-cache-cleaner" + + expect(cron.command).to eq "/usr/bin/glance-cache-cleaner" + expect(cron.minute).to eq "01" + expect(cron.hour).to eq "00" + end + + describe "glance-scrubber-paste.ini" do + before do + @file = @chef_run.template "/etc/glance/glance-scrubber-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + end + + it "uploads qcow images" do + opts = { + :step_into => ["openstack-image_image"] + } + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS.merge(opts) do |n| + n.set["openstack"]["image"] = { + "image_upload" => true, + "upload_images" => [ + "image1" + ], + "upload_image" => { + "image1" => "http://example.com/image.qcow2" + } + } + end + chef_run.converge "openstack-image::api" + cmd = "glance --insecure " \ + "-I glance " \ + "-K glance-pass " \ + "-T service " \ + "-N http://127.0.0.1:5000/v2.0 " \ + "image-create " \ + "--name image1 " \ + "--is-public true " \ + "--container-format bare "\ + "--disk-format qcow2 " \ + "--location http://example.com/image.qcow2" + + expect(chef_run).to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/default_spec.rb b/chef/cookbooks/openstack-image/spec/default_spec.rb new file mode 100644 index 0000000..d9376b5 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/default_spec.rb @@ -0,0 +1,4 @@ +require_relative "spec_helper" + +describe "openstack-dashboard::default" do +end diff --git a/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb new file mode 100644 index 0000000..5b957f8 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/identity_registration_spec.rb @@ -0,0 +1,91 @@ +require_relative "spec_helper" + +describe "openstack-image::identity_registration" do + before do + image_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-image::identity_registration" + end + + it "registers image service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Image Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "image", + :service_description => "Glance Image Service", + :action => [:create_service] + ) + end + + it "registers image endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Image Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "image", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "http://127.0.0.1:9292/v2", + :endpoint_internalurl => "http://127.0.0.1:9292/v2", + :endpoint_publicurl => "http://127.0.0.1:9292/v2", + :action => [:create_endpoint] + ) + end + + it "registers service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :tenant_description => "Service Tenant", + :tenant_enabled => true, + :action => [:create_tenant] + ) + end + + it "registers service user" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register glance User" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "glance", + :user_pass => "glance-pass", + :user_enabled => true, + :action => [:create_user] + ) + end + + it "grants admin role to service user for service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Grant 'admin' Role to glance User for service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :role_name => "admin", + :user_name => "glance", + :action => [:grant_role] + ) + end +end diff --git a/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb b/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb new file mode 100644 index 0000000..e855fc9 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/registry-redhat_spec.rb @@ -0,0 +1,36 @@ +require_relative "spec_helper" + +describe "openstack-image::registry" do + before { image_stubs } + describe "redhat" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @chef_run.converge "openstack-image::registry" + end + + it "installs mysql python packages" do + expect(@chef_run).to install_package "MySQL-python" + end + + it "installs glance packages" do + expect(@chef_run).to upgrade_package "openstack-glance" + expect(@chef_run).to upgrade_package "openstack-swift" + expect(@chef_run).to upgrade_package "cronie" + end + + it "starts glance registry on boot" do + expected = "openstack-glance-registry" + expect(@chef_run).to set_service_to_start_on_boot expected + end + + it "doesn't version the database" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command("glance-manage db_version", false) + chef_run.converge "openstack-image::registry" + cmd = "glance-manage version_control 0" + + expect(chef_run).not_to execute_command cmd + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/registry_spec.rb b/chef/cookbooks/openstack-image/spec/registry_spec.rb new file mode 100644 index 0000000..7e9678c --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/registry_spec.rb @@ -0,0 +1,144 @@ +require_relative "spec_helper" + +describe "openstack-image::registry" do + before { image_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["image"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-image::registry" + end + + expect_runs_openstack_common_logging_recipe + + it "doesn't run logging recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-image::registry" + + expect(chef_run).not_to include_recipe "openstack-common::logging" + end + + expect_installs_python_keystone + + expect_installs_curl + + it "installs mysql python packages" do + expect(@chef_run).to install_package "python-mysqldb" + end + + expect_installs_ubuntu_glance_packages + + expect_creates_cache_dir + + it "starts glance registry on boot" do + expect(@chef_run).to set_service_to_start_on_boot "glance-registry" + end + + describe "version_control" do + before { @cmd = "glance-manage version_control 0" } + + it "versions the database" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command("glance-manage db_version", false) + chef_run.converge "openstack-image::registry" + + expect(chef_run).to execute_command @cmd + end + + it "doesn't version when glance-manage db_version false" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.stub_command("glance-manage db_version", true) + chef_run.converge "openstack-image::registry" + + expect(chef_run).not_to execute_command @cmd + end + end + + it "deletes glance.sqlite" do + expect(@chef_run).to delete_file "/var/lib/glance/glance.sqlite" + end + + expect_creates_glance_dir + + describe "glance-registry.conf" do + before do + @file = @chef_run.template "/etc/glance/glance-registry.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has bind host when bind_interface not specified" do + expect(@chef_run).to create_file_with_content @file.name, + "bind_host = 127.0.0.1" + end + + it "has bind host when bind_interface specified" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["image"]["registry"]["bind_interface"] = "lo" + end + chef_run.converge "openstack-image::registry" + + expect(chef_run).to create_file_with_content @file.name, + "bind_host = 127.0.1.1" + end + + it "notifies image-registry restart" do + expect(@file).to notify "service[image-registry]", :restart + end + end + + describe "db_sync" do + before do + @cmd = "glance-manage db_sync" + end + + it "runs migrations" do + expect(@chef_run).to execute_command @cmd + end + + it "doesn't run migrations" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new(opts) do |n| + n.set["openstack"]["image"]["db"]["migrate"] = false + end + # Lame we must still stub this, since the recipe contains shell + # guards. Need to work on a way to resolve this. + chef_run.stub_command("glance-manage db_version", false) + chef_run.converge "openstack-image::registry" + + expect(chef_run).not_to execute_command @cmd + end + end + + describe "glance-registry-paste.ini" do + before do + @file = @chef_run.template "/etc/glance/glance-registry-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + it "notifies image-registry restart" do + expect(@file).to notify "service[image-registry]", :restart + end + end + end +end diff --git a/chef/cookbooks/openstack-image/spec/spec_helper.rb b/chef/cookbooks/openstack-image/spec/spec_helper.rb new file mode 100644 index 0000000..2edf9c0 --- /dev/null +++ b/chef/cookbooks/openstack-image/spec/spec_helper.rb @@ -0,0 +1,87 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def image_stubs + ::Chef::Recipe.any_instance.stub(:address_for). + with("lo"). + and_return "127.0.1.1" + ::Chef::Recipe.any_instance.stub(:config_by_role). + with("rabbitmq-server", "queue").and_return( + {'host' => 'rabbit-host', 'port' => 'rabbit-port'} + ) + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "openstack_identity_bootstrap_token"). + and_return "bootstrap-token" + ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-image"). + and_return "glance-pass" +end + +def expect_runs_openstack_common_logging_recipe + it "runs logging recipe if node attributes say to" do + expect(@chef_run).to include_recipe "openstack-common::logging" + end +end + +def expect_creates_cache_dir + describe "/var/cache/glance" do + before do + @dir = @chef_run.directory "/var/cache/glance" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end +end + +def expect_installs_python_keystone + it "installs python-keystone package" do + expect(@chef_run).to install_package "python-keystone" + end +end + +def expect_installs_curl + it "installs curl package" do + expect(@chef_run).to install_package "curl" + end +end + +def expect_installs_ubuntu_glance_packages + it "installs glance packages" do + expect(@chef_run).to upgrade_package "glance" + expect(@chef_run).to upgrade_package "python-swift" + end +end + +def expect_creates_glance_dir + describe "/etc/glance" do + before do + @dir = @chef_run.directory "/etc/glance" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "glance", "glance" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end +end diff --git a/chef/cookbooks/openstack-image/templates/default/glance-api-paste.ini.erb b/chef/cookbooks/openstack-image/templates/default/glance-api-paste.ini.erb new file mode 100644 index 0000000..1af607c --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-api-paste.ini.erb @@ -0,0 +1,59 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +# Use this pipeline for no auth or image caching - DEFAULT +[pipeline:glance-api] +pipeline = versionnegotiation unauthenticated-context rootapp + +# Use this pipeline for image caching and no auth +[pipeline:glance-api-caching] +pipeline = versionnegotiation unauthenticated-context cache rootapp + +# Use this pipeline for caching w/ management interface but no auth +[pipeline:glance-api-cachemanagement] +pipeline = versionnegotiation unauthenticated-context cache cachemanage rootapp + +# Use this pipeline for keystone auth +[pipeline:glance-api-keystone] +pipeline = versionnegotiation authtoken context rootapp + +# Use this pipeline for keystone auth with image caching +[pipeline:glance-api-keystone+caching] +pipeline = versionnegotiation authtoken context cache rootapp + +# Use this pipeline for keystone auth with caching and cache management +[pipeline:glance-api-keystone+cachemanagement] +pipeline = versionnegotiation authtoken context cache cachemanage rootapp + +[composite:rootapp] +paste.composite_factory = glance.api:root_app_factory +/: apiversions +/v1: apiv1app +/v2: apiv2app + +[app:apiversions] +paste.app_factory = glance.api.versions:create_resource + +[app:apiv1app] +paste.app_factory = glance.api.v1.router:API.factory + +[app:apiv2app] +paste.app_factory = glance.api.v2.router:API.factory + +[filter:versionnegotiation] +paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory + +[filter:cache] +paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory + +[filter:cachemanage] +paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory + +[filter:context] +paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory + +[filter:unauthenticated-context] +paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory + +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +delay_auth_decision = true diff --git a/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb new file mode 100644 index 0000000..57a7651 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-api.conf.erb @@ -0,0 +1,263 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[DEFAULT] +# Show more verbose log output (sets INFO log level output) +verbose = <%= node["openstack"]["image"]["verbose"] %> + +# Show debugging output in logs (sets DEBUG log level output) +debug = <%= node["openstack"]["image"]["debug"] %> + +# Which backend store should Glance use by default is not specified +# in a request to add a new image to Glance? Default: 'file' +# Available choices are 'file', 'swift', and 's3' +default_store = <%= node["openstack"]["image"]["api"]["default_store"] %> + +# Address to bind the API server +bind_host = <%= @api_bind_address %> + +# Port the bind the API server to +bind_port = <%= @api_bind_port %> + +# Backlog requests when creating socket +backlog = 4096 + +# Number of Glance API worker processes to start. +# On machines with more than one CPU increasing this value +# may improve performance (especially if using SSL with +# compression turned on). It is typically recommended to set +# this value to the number of CPUs present on your machine. +workers = <%= node["cpu"]["total"] %> + +# SQLAlchemy connection string for the reference implementation +# registry server. Any valid SQLAlchemy connection string is fine. +# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine +sql_connection = <%= @sql_connection %> + +# Role used to identify an authenticated user as administrator +#admin_role = admin + +# ================= Syslog Options ============================ + +<% if node["openstack"]["image"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% else %> +# Log to this file. Make sure you do not set the same log +# file for both the API and registry servers! +log_file = /var/log/glance/api.log +<% end %> + + +# ================= SSL Options =============================== + +# Certificate file to use when starting API server securely +# cert_file = /path/to/certfile + +# Private key file to use when starting API server securely +# key_file = /path/to/keyfile + +# ================= Security Options ========================== + +# AES key for encrypting store 'location' metadata, including +# -- if used -- Swift or S3 credentials +# Should be set to a random string of length 16, 24 or 32 bytes +# metadata_encryption_key = <16, 24 or 32 char registry metadata key> + +# ============ Registry Options =============================== + +# Address to find the registry server +registry_host = <%= @registry_ip_address %> + +# Port the registry server is listening on +registry_port = <%= @registry_port %> + +# What protocol to use when connecting to the registry server? +# Set to https for secure HTTP communication +registry_client_protocol = http + +# The path to the key file to use in SSL connections to the +# registry server, if any. Alternately, you may set the +# GLANCE_CLIENT_KEY_FILE environ variable to a filepath of the key file +# registry_client_key_file = /path/to/key/file + +# The path to the cert file to use in SSL connections to the +# registry server, if any. Alternately, you may set the +# GLANCE_CLIENT_CERT_FILE environ variable to a filepath of the cert file +# registry_client_cert_file = /path/to/cert/file + +# The path to the certifying authority cert file to use in SSL connections +# to the registry server, if any. Alternately, you may set the +# GLANCE_CLIENT_CA_FILE environ variable to a filepath of the CA cert file +# registry_client_ca_file = /path/to/ca/file + +# ============ Notification System Options ===================== + +# Notifications can be sent when images are create, updated or deleted. +# There are three methods of sending notifications, logging (via the +# log_file directive), rabbit (via a rabbitmq queue), qpid (via a Qpid +# message queue), or noop (no notifications sent, the default) +notifier_strategy = noop + +# Configuration options if sending notifications via rabbitmq (these are +# the defaults) +rabbit_host = <%= node['openstack']['mq']['bind_address'] %> +rabbit_port = <%= node['openstack']['mq']['port'] %> +rabbit_use_ssl = false +rabbit_userid = <%= node['openstack']['mq']['user'] %> +rabbit_password = <%= node['openstack']['mq']['password'] %> +rabbit_virtual_host = / +rabbit_notification_exchange = glance +rabbit_notification_topic = glance_notifications + +# Configuration options if sending notifications via Qpid (these are +# the defaults) +# qpid_notification_exchange = glance +# qpid_notification_topic = glance_notifications +# qpid_host = localhost +# qpid_port = 5672 +# qpid_username = +# qpid_password = +# qpid_sasl_mechanisms = +# qpid_reconnect_timeout = 0 +# qpid_reconnect_limit = 0 +# qpid_reconnect_interval_min = 0 +# qpid_reconnect_interval_max = 0 +# qpid_reconnect_interval = 0 +# qpid_heartbeat = 5 +# Set to 'ssl' to enable SSL +# qpid_protocol = tcp +# qpid_tcp_nodelay = True + +# ============ Filesystem Store Options ======================== + +# Directory that the Filesystem backend store +# writes image data to +filesystem_store_datadir = /var/lib/glance/images/ + +# ============ Swift Store Options ============================= + + +# Address where the Swift authentication service lives +# Valid schemes are 'http://' and 'https://' +# If no scheme specified, default to 'https://' +#swift_store_auth_address = <%= @swift_store_auth_address %> + +# Authentication version to use. Current Rackspace CloudFiles supports +# Version 1 while swift backed with keystone supports Version 2. +#swift_store_auth_version = <%= @swift_store_auth_version %> + +# User to authenticate against the Swift authentication service +# If you use Swift authentication service, set it to 'account':'user' +# where 'account' is a Swift storage account and 'user' +# is a user in that account +swift_store_user = <%= @swift_user_tenant %>:<%= @swift_store_user %> + +# Auth key for the user authenticating against the +# Swift authentication service +#swift_store_key = <%= @swift_store_key %> + +# Container within the account that the account should use +# for storing images in Swift +#swift_store_container = <%= node["openstack"]["image"]["api"]["swift"]["container"] %> + +# Do we create the container if it does not exist? +#swift_store_create_container_on_put = True + +# What size, in MB, should Glance start chunking image files +# and do a large object manifest in Swift? By default, this is +# the maximum object size in Swift, which is 5GB +#swift_store_large_object_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_size"] %> + +# When doing a large object manifest, what size, in MB, should +# Glance write chunks to Swift? This amount of data is written +# to a temporary disk buffer during the process of chunking +# the image file, and the default is 200MB +#swift_store_large_object_chunk_size = <%= node["openstack"]["image"]["api"]["swift"]["large_object_chunk_size"] %> + +# Whether to use ServiceNET to communicate with the Swift storage servers. +# (If you aren't RACKSPACE, leave this False!) +# +# To use ServiceNET for authentication, prefix hostname of +# `swift_store_auth_address` with 'snet-'. +# Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/ +#swift_enable_snet = False + +# ============ S3 Store Options ============================= + +# Address where the S3 authentication service lives +# Valid schemes are 'http://' and 'https://' +# If no scheme specified, default to 'http://' +#s3_store_host = 127.0.0.1:8080/v1.0/ + +# User to authenticate against the S3 authentication service +#s3_store_access_key = <20-char AWS access key> + +# Auth key for the user authenticating against the +# S3 authentication service +#s3_store_secret_key = <40-char AWS secret key> + +# Container within the account that the account should use +# for storing images in S3. Note that S3 has a flat namespace, +# so you need a unique bucket name for your glance images. An +# easy way to do this is append your AWS access key to "glance". +# S3 buckets in AWS *must* be lowercased, so remember to lowercase +# your AWS access key if you use it in your bucket name below! +#s3_store_bucket = glance + +# Do we create the bucket if it does not exist? +#s3_store_create_bucket_on_put = False + +# When sending images to S3, the data will first be written to a +# temporary buffer on disk. By default the platform's temporary directory +# will be used. If required, an alternative directory can be specified here. +# s3_store_object_buffer_dir = /path/to/dir + +# ============ RBD Store Options ============================= + +# Ceph configuration file path +# If using cephx authentication, this file should +# include a reference to the right keyring +# in a client. section +#rbd_store_ceph_conf = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_ceph_conf"] %> + +# RADOS user to authenticate as (only applicable if using cephx) +#rbd_store_user = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_user"] %> + +# RADOS pool in which images are stored +#rbd_store_pool = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_pool"] %> + +# Images will be chunked into objects of this size (in megabytes). +# For best performance, this should be a power of two +#rbd_store_chunk_size = <%= node["openstack"]["image"]["api"]["rbd"]["rbd_store_chunk_size"] %> + +# ============ Delayed Delete Options ============================= + +# Turn on/off delayed delete +delayed_delete = False + +# Delayed delete time in seconds +scrub_time = 43200 + +# Directory that the scrubber will use to remind itself of what to delete +# Make sure this is also set in glance-scrubber.conf +scrubber_datadir = /var/lib/glance/scrubber + +# =============== Image Cache Options ============================= + +# Base directory that the Image Cache uses +image_cache_dir = /var/lib/glance/image-cache/ + +[keystone_authtoken] +auth_uri = <%= @auth_uri %> +auth_host = <%= @identity_admin_endpoint.host %> +auth_port = <%= @identity_admin_endpoint.port %> +auth_protocol = <%= @identity_admin_endpoint.scheme %> +<% if node["openstack"]["image"]["api"]["auth"]["version"] != "v2.0" %> +auth_version = <%= node["openstack"]["image"]["api"]["auth"]["version"] %> +<% end %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["image"]["api"]["auth"]["cache_dir"] %> + +[paste_deploy] +flavor = <%= @glance_flavor %> diff --git a/chef/cookbooks/openstack-image/templates/default/glance-cache-paste.ini.erb b/chef/cookbooks/openstack-image/templates/default/glance-cache-paste.ini.erb new file mode 100644 index 0000000..9174019 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-cache-paste.ini.erb @@ -0,0 +1,17 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[app:glance-pruner] +paste.app_factory = glance.common.wsgi:app_factory +glance.app_factory = glance.image_cache.pruner:Pruner + +[app:glance-prefetcher] +paste.app_factory = glance.common.wsgi:app_factory +glance.app_factory = glance.image_cache.prefetcher:Prefetcher + +[app:glance-cleaner] +paste.app_factory = glance.common.wsgi:app_factory +glance.app_factory = glance.image_cache.cleaner:Cleaner + +[app:glance-queue-image] +paste.app_factory = glance.common.wsgi:app_factory +glance.app_factory = glance.image_cache.queue_image:Queuer diff --git a/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb new file mode 100644 index 0000000..0b24667 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-cache.conf.erb @@ -0,0 +1,59 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[DEFAULT] +# Show more verbose log output (sets INFO log level output) +verbose = <%= node["openstack"]["image"]["verbose"] %> + +# Show debugging output in logs (sets DEBUG log level output) +debug = <%= node["openstack"]["image"]["debug"] %> + +# =============== Image Cache Options ============================= + +# Directory that the Image Cache writes data to +image_cache_dir = /var/lib/glance/image-cache/ + +# Number of seconds after which we should consider an incomplete image to be +# stalled and eligible for reaping +image_cache_stall_time = 86400 + +# image_cache_invalid_entry_grace_period - seconds +# +# If an exception is raised as we're writing to the cache, the cache-entry is +# deemed invalid and moved to /invalid so that it can be +# inspected for debugging purposes. +# +# This is number of seconds to leave these invalid images around before they +# are elibible to be reaped. +image_cache_invalid_entry_grace_period = 3600 + +# Max cache size in bytes +image_cache_max_size = <%= node["openstack"]["image"]["api"]["cache"]["image_cache_max_size"] %> + +# ================= Syslog Options ============================ + +<% if node["openstack"]["image"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% else %> +log_file = /var/log/glance/image-cache.log +<% end %> + +# ============ Registry Options =============================== + +# Address to find the registry server +registry_host = <%= @registry_ip_address %> + +# Port the registry server is listening on +registry_port = <%= @registry_port %> + +# Auth settings if using Keystone +# auth_url = http://127.0.0.1:5000/v2.0/ +# admin_tenant_name = %SERVICE_TENANT_NAME% +# admin_user = %SERVICE_USER% +# admin_password = %SERVICE_PASSWORD% + +# ================= Security Options ========================== + +# AES key for encrypting store 'location' metadata, including +# -- if used -- Swift or S3 credentials +# Should be set to a random string of length 16, 24 or 32 bytes +# metadata_encryption_key = <16, 24 or 32 char registry metadata key> diff --git a/chef/cookbooks/openstack-image/templates/default/glance-registry-paste.ini.erb b/chef/cookbooks/openstack-image/templates/default/glance-registry-paste.ini.erb new file mode 100644 index 0000000..3e8e752 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-registry-paste.ini.erb @@ -0,0 +1,23 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +# Default minimal pipeline +# Use this pipeline for no auth - DEFAULT +[pipeline:glance-registry] +pipeline = unauthenticated-context registryapp + +# Use this pipeline for keystone auth +[pipeline:glance-registry-keystone] +pipeline = authtoken context registryapp + +[app:registryapp] +paste.app_factory = glance.registry.api.v1:API.factory + +[filter:context] +paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory + +[filter:unauthenticated-context] +paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory + +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +delay_auth_decision = true diff --git a/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb new file mode 100644 index 0000000..b2e911b --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-registry.conf.erb @@ -0,0 +1,74 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[DEFAULT] +# Show more verbose log output (sets INFO log level output) +verbose = <%= node["openstack"]["image"]["verbose"] %> + +# Show debugging output in logs (sets DEBUG log level output) +debug = <%= node["openstack"]["image"]["debug"] %> + +# Address to bind the registry server +bind_host = <%= @registry_bind_address %> + +# Port the bind the registry server to +bind_port = <%= @registry_port %> + +# Backlog requests when creating socket +backlog = 4096 + +# API to use for accessing data. Default value points to sqlalchemy +# package. +data_api = <%= node["openstack"]["image"]["data_api"] %> + +# SQLAlchemy connection string for the reference implementation +# registry server. Any valid SQLAlchemy connection string is fine. +# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine +sql_connection = <%= @sql_connection %> + +# Period in seconds after which SQLAlchemy should reestablish its connection +# to the database. +# +# MySQL uses a default `wait_timeout` of 8 hours, after which it will drop +# idle connections. This can result in 'MySQL Gone Away' exceptions. If you +# notice this, you can lower this value to ensure that SQLAlchemy reconnects +# before MySQL can drop the connection. +sql_idle_timeout = 3600 + +# Limit the api to return `param_limit_max` items in a call to a container. If +# a larger `limit` query param is provided, it will be reduced to this value. +api_limit_max = 1000 + +# If a `limit` query param is not provided in an api request, it will +# default to `limit_param_default` +limit_param_default = 25 + +# ================= Syslog Options ============================ + +<% if node["openstack"]["image"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% else %> +# Log to this file. Make sure you do not set the same log +# file for both the API and registry servers! +log_file = /var/log/glance/registry.log +<% end %> + +# ================= SSL Options =============================== + +# Certificate file to use when starting registry server securely +# cert_file = /path/to/certfile + +# Private key file to use when starting registry server securely +# key_file = /path/to/keyfile + +# ================= Keystone authtoken =============================== +[keystone_authtoken] +auth_host = <%= @identity_endpoint.host %> +auth_port = <%= @identity_endpoint.port %> +auth_protocol = <%= @identity_endpoint.scheme %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["image"]["registry"]["auth"]["cache_dir"] %> + +[paste_deploy] +flavor = keystone diff --git a/chef/cookbooks/openstack-image/templates/default/glance-scrubber-paste.ini.erb b/chef/cookbooks/openstack-image/templates/default/glance-scrubber-paste.ini.erb new file mode 100644 index 0000000..d719b8f --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-scrubber-paste.ini.erb @@ -0,0 +1,5 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[app:glance-scrubber] +paste.app_factory = glance.common.wsgi:app_factory +glance.app_factory = glance.store.scrubber:Scrubber diff --git a/chef/cookbooks/openstack-image/templates/default/glance-scrubber.conf.erb b/chef/cookbooks/openstack-image/templates/default/glance-scrubber.conf.erb new file mode 100644 index 0000000..15a13f5 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/glance-scrubber.conf.erb @@ -0,0 +1,38 @@ +<%= node["openstack"]["image"]["custom_template_banner"] %> + +[DEFAULT] +# Show more verbose log output (sets INFO log level output) +verbose = <%= node["openstack"]["image"]["verbose"] %> + +# Show debugging output in logs (sets DEBUG log level output) +debug = <%= node["openstack"]["image"]["debug"] %> + +<% if node["openstack"]["image"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% else %> +# Log to this file. Make sure you do not set the same log +# file for both the API and registry servers! +log_file = /var/log/glance/scrubber.log +<% end %> + +# Should we run our own loop or rely on cron/scheduler to run us +daemon = False + +# Loop time between checking the db for new items to schedule for delete +wakeup_time = 300 + +# Directory that the scrubber will use to remind itself of what to delete +# Make sure this is also set in glance-api.conf +scrubber_datadir = /var/lib/glance/scrubber + +# Only one server in your deployment should be designated the cleanup host +cleanup_scrubber = False + +# pending_delete items older than this time are candidates for cleanup +cleanup_scrubber_time = 86400 + +# Address to find the registry server for cleanups +registry_host = <%= @registry_ip_address %> + +# Port the registry server is listening on +registry_port = <%= @registry_port %> diff --git a/chef/cookbooks/openstack-image/templates/default/policy.json.erb b/chef/cookbooks/openstack-image/templates/default/policy.json.erb new file mode 100644 index 0000000..9e373ab --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/policy.json.erb @@ -0,0 +1,4 @@ +{ + "default": [], + "manage_image_cache": [["role:admin"]] +} diff --git a/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb b/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb new file mode 100644 index 0000000..e493f75 --- /dev/null +++ b/chef/cookbooks/openstack-image/templates/default/tinyimage.sh.erb @@ -0,0 +1,37 @@ +#!/bin/bash + +OS_USERNAME=<%= @os_username %> +OS_PASSWORD=<%= @os_password %> +OS_TENANT_NAME=<%= @os_tenant_name %> +OS_AUTH_URL=<%= @os_auth_url %> + +glance --os-username=<%= @os_username %> \ + --os-password=<%= @os_password %> \ + --os-tenant-name=<%= @os_tenant_name %> \ + --os-auth-url=<%= @os_auth_url %> \ + image-list |grep "cirros-0.3.1" +if [ $? -ne 0 ]; then + mkdir -p /tmp/images + cd /tmp/images/ + yum install -y wget + wget http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img + glance --os-username=<%= @os_username %> \ + --os-password=<%= @os_password %> \ + --os-tenant-name=<%= @os_tenant_name %> \ + --os-auth-url=<%= @os_auth_url %> \ + image-create \ + --name="cirros-0.3.1-x86_64" \ + --disk-format=qcow2 \ + --container-format bare < /tmp/images/cirros-0.3.1-x86_64-disk.img + + wget http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-i386-disk.img + glance --os-username=<%= @os_username %> \ + --os-password=<%= @os_password %> \ + --os-tenant-name=<%= @os_tenant_name %> \ + --os-auth-url=<%= @os_auth_url %> \ + image-create \ + --name="cirros-0.3.1-i386" \ + --disk-format=qcow2 \ + --container-format bare < /tmp/images/cirros-0.3.1-i386-disk.img +fi + diff --git a/chef/cookbooks/openstack-metering/Berksfile b/chef/cookbooks/openstack-metering/Berksfile new file mode 100644 index 0000000..6ee299f --- /dev/null +++ b/chef/cookbooks/openstack-metering/Berksfile @@ -0,0 +1,6 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" +cookbook "openstack-identity", + git: "git://github.com/stackforge/cookbook-openstack-identity.git" diff --git a/chef/cookbooks/openstack-metering/Berksfile.lock b/chef/cookbooks/openstack-metering/Berksfile.lock new file mode 100644 index 0000000..086f87c --- /dev/null +++ b/chef/cookbooks/openstack-metering/Berksfile.lock @@ -0,0 +1,41 @@ +{ + "sources": { + "openstack-metering": { + "path": "." + }, + "openstack-common": { + "locked_version": "0.4.3", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" + }, + "openstack-identity": { + "locked_version": "7.0.0", + "git": "git://github.com/stackforge/cookbook-openstack-identity.git", + "ref": "b881af26095cfa869a6970067c49597a0ee63586" + }, + "apt": { + "locked_version": "2.0.0" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-metering/CHANGELOG.md b/chef/cookbooks/openstack-metering/CHANGELOG.md new file mode 100644 index 0000000..119e1c7 --- /dev/null +++ b/chef/cookbooks/openstack-metering/CHANGELOG.md @@ -0,0 +1,19 @@ +openstack-metering Cookbook CHANGELOG +============================== +This file is used to list changes made in each version of the openstack-metering cookbook. + + +v7.0.3 +------ +### Bug +- Ubuntu cloud archive dpkg failing to install init script properly for agent-compute + +v7.0.2 +------ +### Improvement +- Add optional host to the ceilometer.conf + +v7.0.1 +------ +### Bug +- Fix naming inconsistency for db password databag. This makes the metering cookbook consistent with all the others. diff --git a/chef/cookbooks/openstack-metering/Gemfile b/chef/cookbooks/openstack-metering/Gemfile new file mode 100644 index 0000000..04ef97e --- /dev/null +++ b/chef/cookbooks/openstack-metering/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-metering/Gemfile.lock b/chef/cookbooks/openstack-metering/Gemfile.lock new file mode 100644 index 0000000..3142523 --- /dev/null +++ b/chef/cookbooks/openstack-metering/Gemfile.lock @@ -0,0 +1,214 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.7) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.9.0) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.2.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.4) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.2.0) + multi_json (1.7.7) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.9) + net-ssh (2.6.8) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.18.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.4) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.4) + rspec-expectations (2.14.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.1) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.6.1) + strainer (3.0.5) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.3) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.1.1) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-metering/README.md b/chef/cookbooks/openstack-metering/README.md new file mode 100644 index 0000000..455e47a --- /dev/null +++ b/chef/cookbooks/openstack-metering/README.md @@ -0,0 +1,81 @@ +Description +=========== + +Installs the OpenStack Metering service **Ceilometer** as part of the OpenStack +reference deployment Chef for OpenStack. Ceilometer is currently installed +from packages. + +https://wiki.openstack.org/wiki/Ceilometer + +Requirements +============ + +Cookbooks +--------- + +Usage +===== + +agent-central +---- +- Installs agent central service. + +agent-compute +---- +- Installs agent compute service. + +api +---- +- Installs API service. + +collector +---- +- Installs nova network service. + +common +---- +- Common metering configuration. + +identity_registration +---- +- Registers the endpoints with Keystone. + +Attributes +========== + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Matt Ray () | +| **Author** | John Dewey () | +| | | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-metering/Strainerfile b/chef/cookbooks/openstack-metering/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-metering/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-metering/attributes/default.rb b/chef/cookbooks/openstack-metering/attributes/default.rb new file mode 100644 index 0000000..90185c2 --- /dev/null +++ b/chef/cookbooks/openstack-metering/attributes/default.rb @@ -0,0 +1,73 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: default +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The name of the Chef role that knows about the message queue server +# that Nova uses +default["openstack"]["metering"]["rabbit_server_chef_role"] = "os-ops-messaging" + +# This user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +default["openstack"]["metering"]["rabbit"]["username"] = "guest" +default["openstack"]["metering"]["rabbit"]["vhost"] = "/" +default["openstack"]["metering"]["rabbit"]["port"] = 5672 +default["openstack"]["metering"]["rabbit"]["host"] = "127.0.0.1" +default["openstack"]["metering"]["rabbit"]["ha"] = false + +default["openstack"]["metering"]["conf_dir"] = "/etc/ceilometer" +default["openstack"]["metering"]["conf"] = ::File.join(node["openstack"]["metering"]["conf_dir"], "ceilometer.conf") +default["openstack"]["metering"]["db"]["username"] = "ceilometer" +default["openstack"]["metering"]["periodic_interval"] = 600 +default["openstack"]["metering"]["syslog"]["use"] = false + +default["openstack"]["metering"]["api"]["auth"]["cache_dir"] = "/var/cache/ceilometer/api" + +default["openstack"]["metering"]["user"] = "ceilometer" +default["openstack"]["metering"]["group"] = "ceilometer" + +default["openstack"]["metering"]["region"] = "RegionOne" + +case platform +when "suse" # :pragma-foodcritic: ~FC024 - won't fix this + default["openstack"]["metering"]["platform"] = { + "common_packages" => ["openstack-ceilometer"], + "agent_central_packages" => ["openstack-ceilometer-agent-central"], + "agent_central_service" => "openstack-ceilometer-agent-central", + "agent_compute_packages" => ["openstack-ceilometer-agent-compute"], + "agent_compute_service" => "openstack-ceilometer-agent-compute", + "api_packages" => ["openstack-ceilometer-api"], + "api_service" => "openstack-ceilometer-api", + "collector_packages" => ["openstack-ceilometer-collector"], + "collector_service" => "openstack-ceilometer-collector" + } +when "ubuntu" + default["openstack"]["metering"]["platform"] = { + "common_packages" => ["ceilometer-common"], + "agent_central_packages" => ["ceilometer-agent-central"], + "agent_central_service" => "ceilometer-agent-central", + "agent_compute_packages" => ["ceilometer-agent-compute"], + "agent_compute_service" => "ceilometer-agent-compute", + "api_packages" => ["ceilometer-api"], + "api_service" => "ceilometer-api", + "collector_packages" => ["ceilometer-collector"], + "collector_service" => "ceilometer-collector" + } +end diff --git a/chef/cookbooks/openstack-metering/files/default/policy.json b/chef/cookbooks/openstack-metering/files/default/policy.json new file mode 100644 index 0000000..373c568 --- /dev/null +++ b/chef/cookbooks/openstack-metering/files/default/policy.json @@ -0,0 +1,3 @@ +{ + "context_is_admin": [["role:admin"]] +} diff --git a/chef/cookbooks/openstack-metering/metadata.rb b/chef/cookbooks/openstack-metering/metadata.rb new file mode 100644 index 0000000..fec5380 --- /dev/null +++ b/chef/cookbooks/openstack-metering/metadata.rb @@ -0,0 +1,21 @@ +name "openstack-metering" +maintainer "AT&T Services, Inc." +maintainer_email "cookbooks@lists.tfoundry.com" +license "Apache 2.0" +description "The OpenStack Metering service Ceilometer." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.3" + +recipe "openstack-metering::agent-central", "Installs agent central service." +recipe "openstack-metering::agent-compute", "Installs agent compute service." +recipe "openstack-metering::api", "Installs API service." +recipe "openstack-metering::collector", "Installs nova network service." +recipe "openstack-metering::common", "Common metering configuration." +recipe "openstack-metering::identity_registration", "Registers the endpoints with Keystone" + +%w{ ubuntu suse }.each do |os| + supports os +end + +depends "openstack-common", "~> 0.4.0" +depends "openstack-identity", "~> 7.0.0" diff --git a/chef/cookbooks/openstack-metering/recipes/agent-central.rb b/chef/cookbooks/openstack-metering/recipes/agent-central.rb new file mode 100644 index 0000000..5ba85b8 --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/agent-central.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: agent-central +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-metering::common" + +platform = node["openstack"]["metering"]["platform"] +platform["agent_central_packages"].each do |pkg| + package pkg +end + +service platform["agent_central_service"] do + action :start +end diff --git a/chef/cookbooks/openstack-metering/recipes/agent-compute.rb b/chef/cookbooks/openstack-metering/recipes/agent-compute.rb new file mode 100644 index 0000000..f9dbf09 --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/agent-compute.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: agent-compute +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-metering::common" + +platform = node["openstack"]["metering"]["platform"] +platform["agent_compute_packages"].each do |pkg| + package pkg +end + +# temp fix for compute-agent init not installing properly ubuntu +# See https://bugs.launchpad.net/cloud-archive/+bug/1221945 +if node["platform"] == "ubuntu" + init_script = "/etc/init/ceilometer-agent-compute.conf" + execute "fix init script" do + command "cp #{init_script}.dpkg-new #{init_script}" + not_if { ::File.exists?(init_script) } + end +end + +service platform["agent_compute_service"] do + action :start +end diff --git a/chef/cookbooks/openstack-metering/recipes/api.rb b/chef/cookbooks/openstack-metering/recipes/api.rb new file mode 100644 index 0000000..ce611f2 --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/api.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: api +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-metering::common" + +directory ::File.dirname(node["openstack"]["metering"]["api"]["auth"]["cache_dir"]) do + owner node["openstack"]["metering"]["user"] + group node["openstack"]["metering"]["group"] + mode 00700 +end + +platform = node["openstack"]["metering"]["platform"] +platform["api_packages"].each do |pkg| + package pkg +end + +service platform["api_service"] do + action :start +end diff --git a/chef/cookbooks/openstack-metering/recipes/collector.rb b/chef/cookbooks/openstack-metering/recipes/collector.rb new file mode 100644 index 0000000..7b068e0 --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/collector.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: collector +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-metering::common" + +conf_switch = "--config-file #{node["openstack"]["metering"]["conf"]}" + +execute "database migration" do + command "ceilometer-dbsync #{conf_switch}" +end + +platform = node["openstack"]["metering"]["platform"] +platform["collector_packages"].each do |pkg| + package pkg +end + +service platform["collector_service"] do + action :start +end diff --git a/chef/cookbooks/openstack-metering/recipes/common.rb b/chef/cookbooks/openstack-metering/recipes/common.rb new file mode 100644 index 0000000..3232e50 --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/common.rb @@ -0,0 +1,89 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: common +# +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +if node["openstack"]["metering"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform = node["openstack"]["metering"]["platform"] +platform["common_packages"].each do |pkg| + package pkg +end + +rabbit_pass = user_password node["openstack"]["metering"]["rabbit"]["username"] + +db_info = db "metering" +db_user = node["openstack"]["metering"]["db"]["username"] +db_pass = db_password "ceilometer" +db_query = db_info["db_type"] == "mysql" ? "?charset=utf8" : "" +db_uri = db_uri("metering", db_user, db_pass).to_s + db_query + +#service_user = node["openstack"]["metering"]["service_user"] +#service_pass = service_password "openstack-compute" +#service_tenant = node["openstack"]["metering"]["service_tenant_name"] + +service_user = node['openstack']['identity']['metering']['username'] +service_pass = service_password node['openstack']['identity']['metering']['password'] +service_tenant = node['openstack']['identity']['metering']['tenant'] + +identity_endpoint = endpoint "identity-api" +image_endpoint = endpoint "image-api" + +Chef::Log.debug("openstack-metering::common:service_user|#{service_user}") +Chef::Log.debug("openstack-metering::common:service_tenant|#{service_tenant}") +Chef::Log.debug("openstack-metering::common:identity_endpoint|#{identity_endpoint.to_s}") + +directory node["openstack"]["metering"]["conf_dir"] do + owner node["openstack"]["metering"]["user"] + group node["openstack"]["metering"]["group"] + mode 00750 + + action :create +end + +template node["openstack"]["metering"]["conf"] do + source "ceilometer.conf.erb" + owner node["openstack"]["metering"]["user"] + group node["openstack"]["metering"]["group"] + mode 00640 + + variables( + :auth_uri => ::URI.decode(identity_endpoint.to_s), + :database_connection => db_uri, + :image_endpoint => image_endpoint, + :identity_endpoint => identity_endpoint, + :rabbit_pass => rabbit_pass, + :service_pass => service_pass, + :service_tenant_name => service_tenant, + :service_user => service_user + ) +end + +cookbook_file "/etc/ceilometer/policy.json" do + source "policy.json" + mode 00640 + owner node["openstack"]["metering"]["user"] + group node["openstack"]["metering"]["group"] +end diff --git a/chef/cookbooks/openstack-metering/recipes/identity_registration.rb b/chef/cookbooks/openstack-metering/recipes/identity_registration.rb new file mode 100644 index 0000000..e37f4cf --- /dev/null +++ b/chef/cookbooks/openstack-metering/recipes/identity_registration.rb @@ -0,0 +1,51 @@ +# +# Cookbook Name:: openstack-metering +# Recipe:: identity_registration +# +# Copyright 2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +api_endpoint = endpoint "metering-api" +identity_admin_endpoint = endpoint "identity-admin" +bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +auth_uri = ::URI.decode identity_admin_endpoint.to_s + +openstack_identity_register "Register Metering Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "ceilometer" + service_type "metering" + service_description "Ceilometer Service" + + action :create_service +end + +openstack_identity_register "Register Metering Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type "metering" + endpoint_region node["openstack"]["metering"]["region"] + endpoint_adminurl ::URI.decode api_endpoint.to_s + endpoint_internalurl ::URI.decode api_endpoint.to_s + endpoint_publicurl ::URI.decode api_endpoint.to_s + + action :create_endpoint +end diff --git a/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb new file mode 100644 index 0000000..e8607f4 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/agent-central-opensuse_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-metering::agent-central" do + before { metering_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-metering::agent-central" + end + + it "installs the agent-central package" do + expect(@chef_run).to install_package "openstack-ceilometer-agent-central" + end + + it "starts the agent-central service" do + expect(@chef_run).to start_service "openstack-ceilometer-agent-central" + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb new file mode 100644 index 0000000..39e995a --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/agent-central_spec.rb @@ -0,0 +1,21 @@ +require_relative "spec_helper" + +describe "openstack-metering::agent-central" do + before { metering_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-metering::agent-central" + end + + expect_runs_common_recipe + + it "installs the agent-central package" do + expect(@chef_run).to install_package "ceilometer-agent-central" + end + + it "starts agent-central service" do + expect(@chef_run).to start_service("ceilometer-agent-central") + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb new file mode 100644 index 0000000..e38ddad --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/agent-compute-opensuse_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-metering::agent-compute" do + before { metering_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-metering::agent-compute" + end + + it "installs the agent-compute package" do + expect(@chef_run).to install_package "openstack-ceilometer-agent-compute" + end + + it "starts the agent-compute service" do + expect(@chef_run).to start_service "openstack-ceilometer-agent-compute" + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb b/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb new file mode 100644 index 0000000..e6f9a3a --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/agent-compute_spec.rb @@ -0,0 +1,21 @@ +require_relative "spec_helper" + +describe "openstack-metering::agent-compute" do + before { metering_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-metering::agent-compute" + end + + expect_runs_common_recipe + + it "installs the agent-compute package" do + expect(@chef_run).to install_package "ceilometer-agent-compute" + end + + it "starts ceilometer-agent-compute service" do + expect(@chef_run).to start_service("ceilometer-agent-compute") + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb new file mode 100644 index 0000000..7f65d70 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/api-opensuse_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-metering::api" do + before { metering_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-metering::api" + end + + it "installs the api package" do + expect(@chef_run).to install_package("openstack-ceilometer-api") + end + + it "starts api service" do + expect(@chef_run).to start_service("openstack-ceilometer-api") + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/api_spec.rb b/chef/cookbooks/openstack-metering/spec/api_spec.rb new file mode 100644 index 0000000..9399b12 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/api_spec.rb @@ -0,0 +1,35 @@ +require_relative "spec_helper" + +describe "openstack-metering::api" do + before { metering_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-metering::api" + end + + expect_runs_common_recipe + + describe "/var/cache/ceilometer" do + before do + @dir = @chef_run.directory "/var/cache/ceilometer" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "ceilometer", "ceilometer" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "700" + end + end + + it "starts api service" do + expect(@chef_run).to start_service("ceilometer-api") + end + + it "starts api service" do + expect(@chef_run).to start_service("ceilometer-api") + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb new file mode 100644 index 0000000..14aac9d --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/collector-opensuse_spec.rb @@ -0,0 +1,19 @@ +require_relative "spec_helper" + +describe "openstack-metering::collector" do + before { metering_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-metering::collector" + end + + it "installs the collector package" do + expect(@chef_run).to install_package "openstack-ceilometer-collector" + end + + it "starts the collector service" do + expect(@chef_run).to start_service "openstack-ceilometer-collector" + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/collector_spec.rb b/chef/cookbooks/openstack-metering/spec/collector_spec.rb new file mode 100644 index 0000000..1655d42 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/collector_spec.rb @@ -0,0 +1,22 @@ +require_relative "spec_helper" + +describe "openstack-metering::collector" do + before { metering_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-metering::collector" + end + + expect_runs_common_recipe + + it "executes ceilometer dbsync" do + command = "ceilometer-dbsync --config-file /etc/ceilometer/ceilometer.conf" + expect(@chef_run).to execute_command command + end + + it "starts collector service" do + expect(@chef_run).to start_service("ceilometer-collector") + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb b/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb new file mode 100644 index 0000000..c3ae818 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/common-opensuse_spec.rb @@ -0,0 +1,15 @@ +require_relative "spec_helper" + +describe "openstack-metering::common" do + before { metering_stubs } + describe "opensuse" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-metering::common" + end + + it "installs the common package" do + expect(@chef_run).to install_package "openstack-ceilometer" + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/common_spec.rb b/chef/cookbooks/openstack-metering/spec/common_spec.rb new file mode 100644 index 0000000..4d7c3fe --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/common_spec.rb @@ -0,0 +1,88 @@ +require_relative "spec_helper" + +describe "openstack-metering::common" do + before { metering_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["metering"]["syslog"]["use"] = true + end + @chef_run.converge "openstack-metering::common" + end + + it "runs logging recipe" do + expect(@chef_run).to include_recipe "openstack-common::logging" + end + + it "installs the common package" do + expect(@chef_run).to install_package "ceilometer-common" + end + + describe "/etc/ceilometer" do + before do + @dir = @chef_run.directory "/etc/ceilometer" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "ceilometer", "ceilometer" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "750" + end + end + + describe "/etc/ceilometer" do + before do + @file = @chef_run.template "/etc/ceilometer/ceilometer.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by("ceilometer", "ceilometer") + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq("640") + end + + it "has rabbit_user" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_userid = guest" + end + + it "has rabbit_password" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_password = rabbit-pass" + end + + it "has rabbit_port" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_port = 5672" + end + + it "has rabbit_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_host = 127.0.0.1" + end + + it "has rabbit_virtual_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_virtual_host = /" + end + end + + describe "/etc/ceilometer/policy.json" do + before do + @dir = @chef_run.cookbook_file "/etc/ceilometer/policy.json" + end + + it "has proper owner" do + expect(@dir).to be_owned_by "ceilometer", "ceilometer" + end + + it "has proper modes" do + expect(sprintf("%o", @dir.mode)).to eq "640" + end + end + end +end diff --git a/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb new file mode 100644 index 0000000..92dea7b --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/identity_registration_spec.rb @@ -0,0 +1,42 @@ +require_relative "spec_helper" + +describe "openstack-metering::identity_registration" do + before do + metering_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-metering::identity_registration" + end + + it "registers metering service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Metering Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_name => "ceilometer", + :service_type => "metering", + :action => [:create_service] + ) + end + + it "registers metering endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Metering Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "metering", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "http://127.0.0.1:8777/v1", + :endpoint_internalurl => "http://127.0.0.1:8777/v1", + :endpoint_publicurl => "http://127.0.0.1:8777/v1", + :action => [:create_endpoint] + ) + end +end diff --git a/chef/cookbooks/openstack-metering/spec/spec_helper.rb b/chef/cookbooks/openstack-metering/spec/spec_helper.rb new file mode 100644 index 0000000..bd0b397 --- /dev/null +++ b/chef/cookbooks/openstack-metering/spec/spec_helper.rb @@ -0,0 +1,37 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def metering_stubs + ::Chef::Recipe.any_instance.stub(:memcached_servers).and_return [] + ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:db_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:user_password). + with("guest"). + and_return "rabbit-pass" + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "openstack_identity_bootstrap_token"). + and_return "bootstrap-token" +end + +def expect_runs_common_recipe + it "runs common recipe" do + expect(@chef_run).to include_recipe "openstack-metering::common" + end +end diff --git a/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb b/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb new file mode 100644 index 0000000..97d0cec --- /dev/null +++ b/chef/cookbooks/openstack-metering/templates/default/ceilometer.conf.erb @@ -0,0 +1,36 @@ +[DEFAULT] +<% if node["openstack"]["metering"]["host"] %> +host = <%= node["openstack"]["metering"]["host"] %> +<% end %> +os_auth_url = <%= @auth_uri %> +os_tenant_name = <%= @service_tenant_name %> +os_password = <%= @service_pass %> +os_username = <%= @service_user %> +policy_file = /etc/ceilometer/policy.json +database_connection = <%= @database_connection %> +rabbit_userid = <%= node["openstack"]["metering"]["rabbit"]["username"] %> +rabbit_password = <%= @rabbit_pass %> +rabbit_port = <%= node["openstack"]["metering"]["rabbit"]["port"] %> +rabbit_host = <%= node["openstack"]["metering"]["rabbit"]["host"] %> +rabbit_virtual_host = <%= node["openstack"]["metering"]["rabbit"]["vhost"] %> +verbose = True +notification_topics = notifications,glance_notifications +rpc_backend = ceilometer.openstack.common.rpc.impl_kombu +<% if node["openstack"]["metering"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> +<% if node["openstack"]["metering"]["debug"] %> +debug = True +<% end %> +glance_registry_host = <%= @image_endpoint.host %> +periodic_interval = <%= node["openstack"]["metering"]["periodic_interval"] %> + +[keystone_authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_host = <%= @identity_endpoint.host %> +auth_port = <%= @identity_endpoint.port %> +auth_protocol = <%= @identity_endpoint.scheme %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["metering"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-network/Berksfile b/chef/cookbooks/openstack-network/Berksfile new file mode 100644 index 0000000..c00fec3 --- /dev/null +++ b/chef/cookbooks/openstack-network/Berksfile @@ -0,0 +1,6 @@ +metadata + +cookbook 'openstack-identity', :git => 'https://github.com/stackforge/cookbook-openstack-identity.git' +cookbook 'openstack-common', :git => 'https://github.com/stackforge/cookbook-openstack-common.git' +cookbook 'database' +cookbook 'mysql' \ No newline at end of file diff --git a/chef/cookbooks/openstack-network/Berksfile.lock b/chef/cookbooks/openstack-network/Berksfile.lock new file mode 100644 index 0000000..773cd83 --- /dev/null +++ b/chef/cookbooks/openstack-network/Berksfile.lock @@ -0,0 +1,41 @@ +{ + "sources": { + "openstack-network": { + "path": "." + }, + "openstack-identity": { + "locked_version": "7.0.0", + "git": "https://github.com/stackforge/cookbook-openstack-identity.git", + "ref": "b881af26095cfa869a6970067c49597a0ee63586" + }, + "openstack-common": { + "locked_version": "0.4.3", + "git": "https://github.com/stackforge/cookbook-openstack-common.git", + "ref": "eb5eed7126b6a6efbaf803e8a594d610cf661e97" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.0" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "apt": { + "locked_version": "2.0.0" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-network/Gemfile b/chef/cookbooks/openstack-network/Gemfile new file mode 100644 index 0000000..bb25f5c --- /dev/null +++ b/chef/cookbooks/openstack-network/Gemfile @@ -0,0 +1,11 @@ +# A sample Gemfile +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef dependency +gem "berkshelf", "~> 2.0.6" +gem "chefspec", "~> 2.0.0" +gem "foodcritic" +gem "strainer" +gem "webmock", "~> 1.11.0" +gem "tailor" diff --git a/chef/cookbooks/openstack-network/Gemfile.lock b/chef/cookbooks/openstack-network/Gemfile.lock new file mode 100644 index 0000000..955443e --- /dev/null +++ b/chef/cookbooks/openstack-network/Gemfile.lock @@ -0,0 +1,223 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.13) + i18n (= 0.6.1) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.6) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (2.0.0) + chef (>= 10.0) + erubis + fauxhai (~> 1.1) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.9.0) + builder (>= 2.1.2) + crack (0.4.0) + safe_yaml (~> 0.9.0) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.1) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.7) + multi_xml (0.5.5) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rak (1.4) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.4) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.5) + rspec-expectations (2.14.2) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.3) + rubyntlm (0.1.1) + safe_yaml (0.9.3) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.6.0) + strainer (3.0.4) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + timers (1.1.0) + tins (0.8.2) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.1.0) + buff-extensions (~> 0.1) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + webmock (1.11.0) + addressable (>= 2.2.7) + crack (>= 0.3.2) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.6) + chef (~> 11.4.4) + chefspec (~> 2.0.0) + foodcritic + json (<= 1.7.7) + strainer + tailor + webmock (~> 1.11.0) diff --git a/chef/cookbooks/openstack-network/README.md b/chef/cookbooks/openstack-network/README.md new file mode 100644 index 0000000..a20a2f7 --- /dev/null +++ b/chef/cookbooks/openstack-network/README.md @@ -0,0 +1,100 @@ +Description +=========== + +This cookbook installs the **OpenStack Network** service (formerly project-named Quantum) +as part of a Chef reference deployment of OpenStack. + +More information about the OpenStack Network service is available +[here](http://docs.openstack.org/trunk/openstack-network/admin/content/index.html) + +Usage +===== + +OpenStack Network's design is modular, with plugins available that handle L2 and +L3 networking for various hardware vendors and standards. + +Requirements +============ + +Chef 11.4.4 or higher required (for Chef environment use) + +Cookbooks +--------- + +The following cookbooks are dependencies: + +* identity +* openstack-common `>= 2.0.0` + +Recipes +======= + +server +------ + +- Installs the openstack-network API server + +dhcp\_agent +-------- + +- Installs the DHCP agent + +l3\_agent +-------- + +- Installs the L3 agent and metadata agent + +Identity-registration +--------------------- + +- Registers the OpenStack Network API endpoint and service user with Keystone + +Attributes +========== + +TODO + +Templates +========= + +* `api-paste.ini.erb` - Paste config for OpenStack Network server +* `quantum.conf.erb` - Config file for OpenStack Network server +* `policy.json.erb` - Configuration of ACLs for glance API server + +Testing +======= + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Authors** | Alan Meadows () | +| | Jay Pipes () | +| | Ionut Artarisi () | +| | | +| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | +| | Copyright (c) 2013, SUSE Linux GmbH | +| | Copyright (c) 2012, Rackspace US, Inc. | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-network/Strainerfile b/chef/cookbooks/openstack-network/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-network/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-network/attributes/default.rb b/chef/cookbooks/openstack-network/attributes/default.rb new file mode 100644 index 0000000..32e12ad --- /dev/null +++ b/chef/cookbooks/openstack-network/attributes/default.rb @@ -0,0 +1,791 @@ +# +# Cookbook Name:: openstack-network +# Attributes:: default +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set to some text value if you want templated config files +# to contain a custom banner at the top of the written file +default["openstack"]["network"]["custom_template_banner"] = " +# This file autogenerated by Chef +# Do not edit, changes will be overwritten +" + +default["openstack"]["network"]["verbose"] = "False" +default["openstack"]["network"]["debug"] = "False" + +# Gets set in the Network Endpoint when registering with Keystone +default["openstack"]["network"]["region"] = "RegionOne" +default["openstack"]["network"]["service_user"] = "quantum" +default["openstack"]["network"]["service_role"] = "admin" +default["openstack"]["network"]["service_name"] = "quantum" +default["openstack"]["network"]["service_type"] = "network" +default["openstack"]["network"]["description"] = "OpenStack Networking service" + +# The rabbit user's password is stored in an encrypted databag +# and accessed with openstack-common cookbook library's +# user_password routine. You are expected to create +# the user, pass, vhost in a wrapper rabbitmq cookbook. +default["openstack"]["network"]["rabbit_server_chef_role"] = "rabbitmq-server" +default["openstack"]["network"]["rabbit"]["username"] = "guest" +default["openstack"]["network"]["rabbit"]["vhost"] = "/" +default["openstack"]["network"]["rabbit"]["port"] = 5672 +default["openstack"]["network"]["rabbit"]["host"] = "127.0.0.1" +default["openstack"]["network"]["rabbit"]["ha"] = false + +# The database username for the quantum database +default["openstack"]["network"]["db"]["username"] = "quantum" + +# Used in the Keystone authtoken middleware configuration +default["openstack"]["network"]["service_tenant_name"] = "service" +default["openstack"]["network"]["service_user"] = "quantum" +default["openstack"]["network"]["service_role"] = "admin" + +# The default agent reporting interval +default["openstack"]["network"]["api"]["agent"]["agent_report_interval"] = 4 + +# The agent signing directory for api server +default["openstack"]["network"]["api"]["agent"]["signing_dir"] = "/var/lib/quantum/keystone-signing" + +# Keystone PKI signing directory. +default["openstack"]["network"]["api"]["auth"]["cache_dir"] = "/var/cache/quantum/api" + +# If bind_interface is set, the quantum API service will bind to the +# address on this interface and use the port in bind_port. Otherwise, +# it will bind to the API endpoint's host. +default["openstack"]["network"]["api"]["bind_interface"] = nil +default["openstack"]["network"]["api"]["bind_port"] = 9696 + +# logging attribute +default["openstack"]["network"]["syslog"]["use"] = false + +# Whether or not we want to disable offloading +# on all the NIC interfaces (currently only supports +# ubuntu and debian). This can help if openvswitch +# or nicira plugins are crashing the sdn routers +default['openstack']['network']['disable_offload'] = false + +# configure quantum ha tool installation parameters +default["openstack"]["network"]["quantum_ha_cmd_cron"] = false +default["openstack"]["network"]["quantum_ha_cmd"] = "/usr/local/bin/quantum-ha-tool.py" +default["openstack"]["network"]["cron_l3_healthcheck"] = "*/1" +default["openstack"]["network"]["cron_replicate_dhcp"] = "*/1" + +# the plugins to install on the server. this will be +# quantum-plugin-%plugin% and the first plugin in the +# list should match the core plugin below +# N.B. this will be ignored on SUSE as all plugins are installed by +# default by the main openstack-quantum package +default["openstack"]["network"]["plugins"] = ['openvswitch', 'openvswitch-agent' ] + +# the core plugin to use for quantum +default["openstack"]["network"]["core_plugin"] = "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2" + +# The bridging interface driver. +# +# Options are: +# +# - quantum.agent.linux.interface.OVSInterfaceDriver +# - quantum.agent.linux.interface.BridgeInterfaceDriver +# + +default["openstack"]["network"]["interface_driver"] = 'quantum.agent.linux.interface.OVSInterfaceDriver' + +# maps the above driver to a plugin name +default["openstack"]["network"]["interface_driver_map"] = { + 'ovsinterfacedriver' => 'openvswitch', + 'bridgeinterfacedriver' => 'linuxbridge' +} + +default["openstack"]["network"]["plugin_conf_map"] = { + 'ovsinterfacedriver' => 'openvswitch/ovs_quantum_plugin.ini', + 'bridgeinterfacedriver' => 'linuxbridge/linuxbridge_conf.ini' +} + +# The agent can use other DHCP drivers. Dnsmasq is the simplest and requires +# no additional setup of the DHCP server. +default["openstack"]["network"]["dhcp_driver"] = 'quantum.agent.linux.dhcp.Dnsmasq' + +# Use namespaces and optionally allow overlapping IPs. You +# must enable namespaces to use overlapping ips. Also, +# you must have kernel build with CONFIG_NET_NS=y and +# iproute2 package that supports namespaces. +default["openstack"]["network"]["use_namespaces"] = "False" +default["openstack"]["network"]["allow_overlapping_ips"] = "False" + +# use quantum root wrap +default["openstack"]["network"]["use_rootwrap"] = true + +# ============================= DHCP Agent Configuration =================== + +# The scheduler class to use for scheduling to DHCP agents +default["openstack"]["network"]["dhcp"]["scheduler"] = "quantum.scheduler.dhcp_agent_scheduler.ChanceScheduler" + +# Override the default mtu setting given to virtual machines +# to 1454 to allow for tunnel and other encapsulation overhead. You +# can adjust this from 1454 to 1500 if you do not want any lowering +# of the default guest MTU. +default["openstack"]["network"]["dhcp"]["dhcp-option"] = "26,1454" + +# Number of seconds between sync of DHCP agent with Quantum API server +default["openstack"]["network"]["dhcp"]["resync_interval"] = 5 + +# OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS +# as OpenFlow switch and check port status +default["openstack"]["network"]["dhcp"]["ovs_use_veth"] = "True" + +# The DHCP server can assist with providing metadata support on isolated +# networks. Setting this value to True will cause the DHCP server to append +# specific host routes to the DHCP request. The metadata service will only +# be activated when the subnet gateway_ip is None. The guest instance must +# be configured to request host routes via DHCP (Option 121). +default["openstack"]["network"]["dhcp"]["enable_isolated_metadata"] = "False" + +# Allows for serving metadata requests coming from a dedicated metadata +# access network whose cidr is 169.254.169.254/16 (or larger prefix), and +# is connected to a Quantum router from which the VMs send metadata +# request. In this case DHCP Option 121 will not be injected in VMs, as +# they will be able to reach 169.254.169.254 through a router. +# This option requires enable_isolated_metadata = True +default["openstack"]["network"]["dhcp"]["enable_metadata_network"] = "False" + +# On ubuntu precise, we build dnsmasq from source to fetch a more recent +# version of dnsmasq since a backport is not available. For any other +# platform, dnsmasq will be installed as a package +# +# See https://lists.launchpad.net/openstack/msg11696.html +default["openstack"]["network"]["dhcp"]["dnsmasq_url"] = "https://github.com/guns/dnsmasq/archive/v2.65.tar.gz" + +# The name of the file we will fetch +default["openstack"]["network"]["dhcp"]["dnsmasq_filename"] = "v2.65.tar.gz" + +# The checksum of the remote file we fetched +default["openstack"]["network"]["dhcp"]["dnsmasq_checksum"] = "f6cab8c64cb612089174f50927a05e2b" + +# The package architecture that will be built which should match the +# archecture of the server this cookbook will run on which will be +# amd64 or i386 +default["openstack"]["network"]["dhcp"]["dnsmasq_architecture"] = "amd64" + +# The debian package version that the above tarball will produce +default["openstack"]["network"]["dhcp"]["dnsmasq_dpkgversion"] = "2.65-1" + +# Upstream resolver to use +# This will be used by dnsmasq to resolve recursively +# but will not be used if the tenant specifies a dns +# server in their subnet +# +# Defaults are spread out across multiple, presumably +# reliable, upstream providers +# +# 8.8.8.8 is Google +# 209.244.0.3 is Level3 +# +# May be a comma separated list of servers +default["openstack"]["network"]["dhcp"]["upstream_dns_servers"] = ["8.8.8.8", "209.244.0.3"] + +# Set the default domain in dnsmasq +default["openstack"]["network"]["dhcp"]["default_domain"] = "openstacklocal" + +# ============================= L3 Agent Configuration ===================== + +# The scheduler class to use for scheduling routers to L3 agents +default["openstack"]["network"]["l3"]["scheduler"] = "quantum.scheduler.l3_agent_scheduler.ChanceScheduler" + +# If use_namespaces is set as False then the agent can only configure one router. +# This is done by setting the specific router_id. +default["openstack"]["network"]["l3"]["router_id"] = nil + +# Each L3 agent can be associated with at most one external network. This +# value should be set to the UUID of that external network. If empty, +# the agent will enforce that only a single external networks exists and +# use that external network id +default["openstack"]["network"]["l3"]["gateway_external_network_id"] = nil + +# Indicates that this L3 agent should also handle routers that do not have +# an external network gateway configured. This option should be True only +# for a single agent in a Quantum deployment, and may be False for all agents +# if all routers must have an external network gateway +default["openstack"]["network"]["l3"]["handle_internal_only_routers"] = "True" + +# Name of bridge used for external network traffic. This should be set to +# empty value for the linux bridge +default["openstack"]["network"]["l3"]["external_network_bridge"] = "br-ex" + +# Interface to use for external bridge. +default["openstack"]["network"]["l3"]["external_network_bridge_interface"] = "eth1" + +# TCP Port used by Quantum metadata server +default["openstack"]["network"]["l3"]["metadata_port"] = 9697 + +# Send this many gratuitous ARPs for HA setup. Set it below or equal to 0 +# to disable this feature. +default["openstack"]["network"]["l3"]["send_arp_for_ha"] = 3 + +# seconds between re-sync routers' data if needed +default["openstack"]["network"]["l3"]["periodic_interval"] = 40 + +# seconds to start to sync routers' data after +# starting agent +default["openstack"]["network"]["l3"]["periodic_fuzzy_delay"] = 5 + +# ============================= Metadata Agent Configuration =============== + +# The location of the Nova Metadata API service to proxy to (nil uses default) +default["openstack"]["network"]["metadata"]["nova_metadata_ip"] = "127.0.0.1" +default["openstack"]["network"]["metadata"]["nova_metadata_port"] = 8775 + +# The name of the secret databag containing the metadata secret +default["openstack"]["network"]["metadata"]["secret_name"] = "quantum_metadata_secret" + + +# ============================= LBaaS Agent Configuration ================== + +# Enable or disable quantum loadbalancer +default["openstack"]["network"]["quantum_loadbalancer"] = false + +# Plugin configuration path +default["openstack"]["network"]["lbaas_config_path"] = "/etc/quantum/plugins/services/agent_loadbalancer" + +# Number of seconds between sync of LBaaS agent with Quantum API server +default["openstack"]["network"]["lbaas"]["periodic_interval"] = 10 + +# Set lbaas plugin +# Supported types are: "ovs" (ovs based plugins(OVS, Ryu, NEC, NVP, BigSwitch/Floodlight)) +# and "linuxbridge". +default["openstack"]["network"]["lbaas_plugin"] = "ovs" + +# ============================= OVS Plugin Configuration =================== + +# Type of network to allocate for tenant networks. The default value 'local' is +# useful only for single-box testing and provides no connectivity between hosts. +# You MUST either change this to 'vlan' and configure network_vlan_ranges below +# or change this to 'gre' and configure tunnel_id_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to disable creation +# of tenant networks. +default["openstack"]["network"]["openvswitch"]["tenant_network_type"] = 'gre' + +# Comma-separated list of [::] tuples enumerating +# ranges of VLAN IDs on named physical networks that are available for allocation. +# All physical networks listed are available for flat and VLAN provider network +# creation. Specified ranges of VLAN IDs are available for tenant network +# allocation if tenant_network_type is 'vlan'. If empty, only gre and local +# networks may be created +# +# Example: network_vlan_ranges = physnet1:1000:2999 +default["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] = nil + +# Set to True in the server and the agents to enable support +# for GRE networks. Requires kernel support for OVS patch ports and +# GRE tunneling. +default["openstack"]["network"]["openvswitch"]["enable_tunneling"] = "True" + +# Comma-separated list of : tuples +# enumerating ranges of GRE tunnel IDs that are available for tenant +# network allocation if tenant_network_type is 'gre'. +# +# Example: tunnel_id_ranges = 1:1000 +default["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] = "1:10000" + +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch bay". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity +default["openstack"]["network"]["openvswitch"]["integration_bridge"] = 'br-int' + +# Only used for the agent if tunnel_id_ranges (above) is not empty for +# the server. In most cases, the default value should be fine +default["openstack"]["network"]["openvswitch"]["tunnel_bridge"] = "br-tun" + +# Peer patch port in integration bridge for tunnel bridge (nil uses default) +default["openstack"]["network"]["openvswitch"]["int_peer_patch_port"] = nil + +# Peer patch port in tunnel bridge for integration bridge (nil uses default) +default["openstack"]["network"]["openvswitch"]["tun_peer_patch_port"] = nil + +# Uncomment this line for the agent if tunnel_id_ranges (above) is not +# empty for the server. Set local_ip to be the local IP address of +# this hypervisor or set the local_ip_interface parameter to use the IP +# address of the specified interface. If local_ip_interface is set +# it will take precedence. +default["openstack"]["network"]["openvswitch"]["local_ip"] = "127.0.0.1" +default["openstack"]["network"]["openvswitch"]["local_ip_interface"] = nil + +# Comma-separated list of : tuples +# mapping physical network names to the agent's node-specific OVS +# bridge names to be used for flat and VLAN networks. The length of +# bridge names should be no more than 11. Each bridge must +# exist, and should have a physical network interface configured as a +# port. All physical networks listed in network_vlan_ranges on the +# server should have mappings to appropriate bridges on each agent. +# +# Example: bridge_mappings = physnet1:br-eth1 +default["openstack"]["network"]["openvswitch"]["bridge_mappings"] = nil + +# Firewall driver for realizing quantum security group function +default["openstack"]["network"]["openvswitch"]["fw_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" + +# ============================= LinuxBridge Plugin Configuration =========== + +# Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST change this to +# 'vlan' and configure network_vlan_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to +# disable creation of tenant networks. +default["openstack"]["network"]["linuxbridge"]["tenant_network_type"] = 'local' + +# Comma-separated list of [::] tuples enumerating +# ranges of VLAN IDs on named physical networks that are available for allocation. +# All physical networks listed are available for flat and VLAN provider network +# creation. Specified ranges of VLAN IDs are available for tenant network +# allocation if tenant_network_type is 'vlan'. If empty, only gre and local +# networks may be created. +# +# Example: network_vlan_ranges = physnet1:1000:2999 +default["openstack"]["network"]["linuxbridge"]["network_vlan_ranges"] = "" + +# (ListOpt) Comma-separated list of +# : tuples mapping physical +# network names to the agent's node-specific physical network +# interfaces to be used for flat and VLAN networks. All physical +# networks listed in network_vlan_ranges on the server should have +# mappings to appropriate interfaces on each agent. +# +# Example: physical_interface_mappings = physnet1:eth1 +default["openstack"]["network"]["linuxbridge"]["physical_interface_mappings"] = "" + +# ============================= BigSwitch Plugin Configuration ============= + +# Not really sure what this is... +default["openstack"]["network"]["bigswitch"]["servers"] = "localhost:8080" + +# ============================= Brocade Plugin Configuration =============== + +# username = +default["openstack"]["network"]["brocade"]["switch_username"] = "admin" + +# password = +default["openstack"]["network"]["brocade"]["switch_password"] = "admin" + +# address = +default["openstack"]["network"]["brocade"]["switch_address"] = "127.0.0.1" + +# ostype = NOS +default["openstack"]["network"]["brocade"]["switch_ostype"] = "NOS" + +# physical_interface = +# +# Example: +# physical_interface = physnet1 +default["openstack"]["network"]["brocade"]["physical_interface"] = "physnet1" + +# (ListOpt) Comma-separated list of +# [::] tuples enumerating ranges +# of VLAN IDs on named physical networks that are available for +# allocation. All physical networks listed are available for flat and +# VLAN provider network creation. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 +default["openstack"]["network"]["brocade"]["network_vlan_ranges"] = "" + +# (ListOpt) Comma-separated list of +# : tuples mapping physical +# network names to the agent's node-specific physical network +# interfaces to be used for flat and VLAN networks. All physical +# networks listed in network_vlan_ranges on the server should have +# mappings to appropriate interfaces on each agent. +# +# Example: physical_interface_mappings = physnet1:eth1 +default["openstack"]["network"]["brocade"]["physical_interface_mappings"] = "" + +# ============================= Cisco Plugin Configuration ================= + +# The module and class name path for the nexus plugin +default["openstack"]["network"]["cisco"]["nexus_plugin"] = "quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin" + +# The module and class name path for the vswitch plugin +default["openstack"]["network"]["cisco"]["vswitch_plugin"] = "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2" + +# Start of the tenant VLAN range +default["openstack"]["network"]["cisco"]["vlan_start"] = 100 + +# End of the tenant VLAN range +default["openstack"]["network"]["cisco"]["vlan_end"] = 3000 + +# Prefix for tenant VLANs +default["openstack"]["network"]["cisco"]["vlan_name_prefix"] = "q-" + +# Maximum number of ports +default["openstack"]["network"]["cisco"]["max_ports"] = 100 +# Max number of port profiles +default["openstack"]["network"]["cisco"]["max_port_profiles"] = 65568 + +# Maximum number of networks +default["openstack"]["network"]["cisco"]["max_networks"] = 65568 + +# Module and class path for switch model +default["openstack"]["network"]["cisco"]["model_class"] = "quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2" + +# Module and class path for VLAN network manager +default["openstack"]["network"]["cisco"]["manager_class"] = "quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr" + +# Module and class path for the Nexus driver +default["openstack"]["network"]["cisco"]["nexus_driver"] = "quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver" + +# For each Nexus switch, add a hash to the +# node["openstack"]["network"]["cisco"]["nexus_switches"] Hash, +# using the switch's IP address as the outer Hash key with each +# hash containing this information: +# +# - ssh_port= +# - username= +# - password= +# - hosts = [ (,), ... ] +# +# Example: +# +# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["ssh_port"] = 22 +# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["username"] = "admin" +# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["password"] = "mySecretPassword" +# node["openstack"]["network"]["cisco"]["nexus_switches"]["1.1.1.1"]["hosts"] = [ [ "compute1", "1/1" ], +# [ "compute2", "1/2" ]] +# +# +# will write the following to the Cisco plugin config INI file: +# [NEXUS_SWITCH:1.1.1.1] +# compute1=1/1 +# compute2=1/2 +# ssh_port=22 +# username=admin +# password=mySecretPassword +# +default["openstack"]["network"]["cisco"]["nexus_switches"] = {} + +# ============================= Hyper-V Plugin Configuration =============== + +# Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST change this to +# 'vlan' and configure network_vlan_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to +# disable creation of tenant networks. +default["openstack"]["network"]["hyperv"]["tenant_network_type"] = 'local' + +# Comma-separated list of [::] tuples enumerating +# ranges of VLAN IDs on named physical networks that are available for allocation. +# All physical networks listed are available for flat and VLAN provider network +# creation. Specified ranges of VLAN IDs are available for tenant network +# allocation if tenant_network_type is 'vlan'. If empty, only gre and local +# networks may be created. +# +# Example: network_vlan_ranges = physnet1:1000:2999 +default["openstack"]["network"]["hyperv"]["network_vlan_ranges"] = "" + +# Agent's polling interval in seconds +default["openstack"]["network"]["hyperv"]["polling_interval"] = 2 + +# (ListOpt) Comma separated list of : +# where the physical networks can be expressed with wildcards, +# e.g.: ."*:external". +# The referred external virtual switches need to be already present on +# the Hyper-V server. +# If a given physical network name will not match any value in the list +# the plugin will look for a virtual switch with the same name. +# +# Default: physical_network_vswitch_mappings = *:external +# Example: physical_network_vswitch_mappings = net1:external1,net2:external2 +default["openstack"]["network"]["hyperv"]["physical_network_vswitch_mappings"] = "*:external" + +# (StrOpt) Private virtual switch name used for local networking. +# +# Default: local_network_vswitch = private +# Example: local_network_vswitch = custom_vswitch +default["openstack"]["network"]["hyperv"]["local_network_vswitch"] = "private" + +# ============================= Metaplugin Plugin Configuration ============ + +## This is list of flavor:quantum_plugins +# extension method is used in the order of this list +default["openstack"]["network"]["metaplugin"]["plugin_list"] = "openvswitch:quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2,linuxbridge:quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2" +default["openstack"]["network"]["metaplugin"]["l3_plugin_list"] = "openvswitch:quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2,linuxbridge:quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2" + +# Default "flavor" for L2 and L3 +default["openstack"]["network"]["metaplugin"]["default_flavor"] = "openvswitch" +default["openstack"]["network"]["metaplugin"]["default_l3_flavor"] = "openvswitch" + +# ============================= Midonet Plugin Configuration =============== + +# MidoNet API server URI +default["openstack"]["network"]["midonet"]["midonet_uri"] = "http://localhost:8080/midonet-api" + +# MidoNet admin username +default["openstack"]["network"]["midonet"]["username"] = "admin" + +# MidoNet admin password +default["openstack"]["network"]["midonet"]["password"] = "passw0rd" + +# ID of the project that MidoNet admin user belongs to +default["openstack"]["network"]["midonet"]["project_id"] = "77777777-7777-7777-7777-777777777777" + +# Virtual provider router ID +default["openstack"]["network"]["midonet"]["provider_router_id"] = "00112233-0011-0011-0011-001122334455" + +# Virtual metadata router ID +default["openstack"]["network"]["midonet"]["metadata_router_id"] = "ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa" + +# ============================= NEC Plugin Configuration =================== + +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch port". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity. +default["openstack"]["network"]["nec"]["integration_bridge"] = "br-int" + +# Agent's polling interval in seconds +default["openstack"]["network"]["nec"]["polling_interval"] = 2 + +# Firewall driver for realizing quantum security group function +default["openstack"]["network"]["nec"]["firewall_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" + +# Specify OpenFlow Controller Host, Port and Driver to connect. +default["openstack"]["network"]["nec"]["ofc_host"] = "127.0.0.1" +default["openstack"]["network"]["nec"]["ofc_port"] = 8888 + +# Drivers are in quantum/plugins/nec/drivers/ . +default["openstack"]["network"]["nec"]["ofc_driver"] = "trema" + +# PacketFilter is available when it's enabled in this configuration +# and supported by the driver. +default["openstack"]["network"]["nec"]["ofc_enable_packet_filter"] = "true" + +# ============================= Nicira Plugin Configuration ================ + +# User name for NVP controller +default["openstack"]["network"]["nicira"]["nvp_user"] = "admin" + +# Password for NVP controller +default["openstack"]["network"]["nicira"]["nvp_password"] = "admin" + +# Total time limit for a cluster request +# (including retries across different controllers) +default["openstack"]["network"]["nicira"]["req_timeout"] = 30 + +# Time before aborting a request on an unresponsive controller +default["openstack"]["network"]["nicira"]["http_timeout"] = 10 + +# Maximum number of times a particular request should be retried +default["openstack"]["network"]["nicira"]["retries"] = 2 + +# Maximum number of times a redirect response should be followed +default["openstack"]["network"]["nicira"]["redirects"] = 2 + +# Comma-separated list of NVP controller endpoints (:). When port +# is omitted, 443 is assumed. This option MUST be specified, e.g.: +default["openstack"]["network"]["nicira"]["nvp_controllers"] = "xx.yy.zz.ww:443, aa.bb.cc.dd, ee.ff.gg.hh.ee:80" + +# UUID of the pre-existing default NVP Transport zone to be used for creating +# tunneled isolated "Quantum" networks. This option MUST be specified, e.g.: +default["openstack"]["network"]["nicira"]["default_tz_uuid"] = "1e8e52cf-fa7f-46b0-a14a-f99835a9cb53" + +# (Optional) UUID of the cluster in NVP. It can be retrieved from NVP management +# console "admin" section. +default["openstack"]["network"]["nicira"]["nvp_cluster_uuid"] = "615be8e4-82e9-4fd2-b4b3-fd141e51a5a7" + +# (Optional) UUID for the default l3 gateway service to use with this cluster. +# To be specified if planning to use logical routers with external gateways. +default["openstack"]["network"]["nicira"]["default_l3_gw_service_uuid"] = "" + +# (Optional) UUID for the default l2 gateway service to use with this cluster. +# To be specified for providing a predefined gateway tenant for connecting their networks. +default["openstack"]["network"]["nicira"]["default_l2_gw_service_uuid"] = "" + +# Name of the default interface name to be used on network-gateway. This value +# will be used for any device associated with a network gateway for which an +# interface name was not specified +default["openstack"]["network"]["nicira"]["default_iface_name"] = "breth0" + +# number of network gateways allowed per tenant, -1 means unlimited +default["openstack"]["network"]["nicira"]["quota_network_gateway"] = 5 + +# Maximum number of ports for each bridged logical switch +default["openstack"]["network"]["nicira"]["max_lp_per_bridged_ls"] = 64 + +# Maximum number of ports for each overlay (stt, gre) logical switch +default["openstack"]["network"]["nicira"]["max_lp_per_overlay_ls"] = 256 + +# Number of connects to each controller node. +default["openstack"]["network"]["nicira"]["concurrent_connections"] = 3 + +# Acceptable values for 'metadata_mode' are: +# - 'access_network': this enables a dedicated connection to the metadata +# proxy for metadata server access via Quantum router. +# - 'dhcp_host_route': this enables host route injection via the dhcp agent. +# This option is only useful if running on a host that does not support +# namespaces otherwise access_network should be used. +default["openstack"]["network"]["nicira"]["metadata_mode"] = "access_network" + +# ============================= PLUMGrid Plugin Configuration ============== + +# This line should be pointing to the NOS server, +# for the PLUMgrid platform. In other deployments, +# this is known as controller +default["openstack"]["network"]["plumgrid"]["nos_server"] = "127.0.0.1" +default["openstack"]["network"]["plumgrid"]["nos_server_port"] = "" + +# Authentification parameters for the NOS server. +# These are the admin credentials to manage and control +# the NOS server. +default["openstack"]["network"]["plumgrid"]["username"] = "" +default["openstack"]["network"]["plumgrid"]["password"] = "" +default["openstack"]["network"]["plumgrid"]["servertimeout"] = 5 + +# Name of the network topology to be deployed by NOS +default["openstack"]["network"]["plumgrid"]["topologyname"] = "" + +# ============================= Ryu Plugin Configuration =================== + +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch port". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity. +default["openstack"]["network"]["ryu"]["integration_bridge"] = "br-int" + +# openflow_rest_api = : +default["openstack"]["network"]["ryu"]["openflow_rest_api"] = "127.0.0.1:8080" + +# tunnel key range: 0 < tunnel_key_min < tunnel_key_max +# VLAN: 12bits, GRE, VXLAN: 24bits +default["openstack"]["network"]["ryu"]["tunnel_key_min"] = 1 +default["openstack"]["network"]["ryu"]["tunnel_key_max"] = "0xffffff" + +# tunnel_ip = +# tunnel_interface = interface for tunneling +# when tunnel_ip is NOT specified, ip address is read +# from this interface +default["openstack"]["network"]["ryu"]["tunnel_ip"] = "" +default["openstack"]["network"]["ryu"]["tunnel_interface"] = "eth0" + +# ovsdb_port = port number on which ovsdb is listening +# ryu-agent uses this parameter to setup ovsdb. +# ovs-vsctl set-manager ptcp: +# See set-manager section of man ovs-vsctl for details. +# currently ptcp is only supported. +# ovsdb_ip = +# ovsdb_interface = interface for ovsdb +# when ovsdb_addr NOT specifiied, ip address is gotten +# from this interface +default["openstack"]["network"]["ryu"]["ovsdb_port"] = 6634 +default["openstack"]["network"]["ryu"]["ovsdb_ip"] = "" +default["openstack"]["network"]["ryu"]["ovsdb_interface"] = "eth0" + +# Firewall driver for realizing quantum security group function +default["openstack"]["network"]["ryu"]["firewall_driver"] = "quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" + +# Agent's polling interval in seconds +default["openstack"]["network"]["ryu"]["polling_interval"] = 2 + +# platform-specific settings +case platform +when "fedora", "redhat", "centos" # :pragma-foodcritic: ~FC024 - won't fix this + default["openstack"]["network"]["platform"] = { + "user" => "quantum", + "group" => "quantum", + "mysql_python_packages" => [ "MySQL-python" ], + "postgresql_python_packages" => ["python-psycopg2"], + "nova_network_packages" => [ "openstack-nova-network" ], + "quantum_packages" => [ "openstack-quantum" ], + "quantum_client_packages" => [], + "quantum_dhcp_packages" => [ "openstack-quantum" ], + "quantum_dhcp_build_packages" => [], + "quantum_l3_packages" => [ "openstack-quantum" ], + "quantum_openvswitch_packages" => ["openvswitch", "bridge-utils"], + "quantum_openvswitch_agent_packages" => ["openstack-quantum-openvswitch"], + "quantum_linuxbridge_agent_packages" => ["openstack-quantum-linuxbridge"], + "quantum_metadata_agent_packages" => [], + "quantum_plugin_package" => "openstack-quantum-%plugin%", + "quantum_server_packages" => [], + "quantum_dhcp_agent_service" => "quantum-dhcp-agent", + "quantum_l3_agent_service" => "quantum-l3-agent", + "quantum_metadata_agent_service" => "quantum-metadata-agent", + "quantum_openvswitch_service" => "openvswitch", + "quantum_openvswitch_agent_service" => "quantum-openvswitch-agent", + "quantum_linuxbridge_agent_service" => "quantum-linuxbridge-agent", + "quantum_server_service" => "quantum-server", + "package_overrides" => "" + } +when "suse" + default["openstack"]["network"]["platform"] = { + "user" => "openstack-quantum", + "group" => "openstack-quantum", + "mysql_python_packages" => ["python-mysql"], + "postgresql_python_packages" => ["python-psycopg2"], + "nova_network_packages" => ["openstack-nova-network"], + "quantum_packages" => ["openstack-quantum"], + "quantum_client_packages" => [], + "quantum_dhcp_packages" => ["openstack-quantum-dhcp-agent"], + "quantum_dhcp_build_packages" => [], + "quantum_l3_packages" => ["openstack-quantum-l3-agent"], + # plugins are installed by the main openstack-quantum package on SUSE + "quantum_plugin_package" => "", + "quantum_metadata_agent_packages" => ["openstack-quantum-metadata-agent"], + "quantum_openvswitch_packages" => ["openvswitch-switch"], + "quantum_openvswitch_agent_packages" => ["openstack-quantum-openvswitch-agent"], + "quantum_linuxbridge_agent_packages" => ["openstack-quantum-linuxbridge-agent"], + "quantum_metadata_agent_packages" => ["openstack-quantum-metadata-agent"], + "quantum_server_packages" => [], + "quantum_dhcp_agent_service" => "openstack-quantum-dhcp-agent", + "quantum_l3_agent_service" => "openstack-quantum-l3-agent", + "quantum_metadata_agent_service" => "openstack-quantum-metadata-agent", + "quantum_openvswitch_service" => "openvswitch-switch", + "quantum_openvswitch_agent_service" => "openstack-quantum-openvswitch-agent", + "quantum_linuxbridge_agent_service" => "openstack-quantum-linuxbridge-agent", + "quantum_server_service" => "openstack-quantum", + "package_overrides" => "" + } +when "ubuntu" + default["openstack"]["network"]["platform"] = { + "user" => "quantum", + "group" => "quantum", + "mysql_python_packages" => [ "python-mysqldb" ], + "postgresql_python_packages" => [ "python-psycopg2" ], + "nova_network_packages" => [ "nova-network" ], + "quantum_lb_packages" => ["quantum-lbaas-agent", "haproxy"], + "quantum_packages" => [ "quantum-common", "python-pyparsing", "python-cliff" ], + "quantum_client_packages" => [ "python-quantumclient", "python-pyparsing" ], + "quantum_dhcp_packages" => [ "quantum-dhcp-agent" ], + "quantum_dhcp_build_packages" => [ "build-essential", "pkg-config", "libidn11-dev", "libdbus-1-dev", "libnetfilter-conntrack-dev", "gettext" ], + "quantum_l3_packages" => [ "quantum-l3-agent" ], + "quantum_openvswitch_packages" => [ "openvswitch-switch", "openvswitch-datapath-dkms", "bridge-utils" ], + "quantum_openvswitch_agent_packages" => [ "quantum-plugin-openvswitch", "quantum-plugin-openvswitch-agent" ], + "quantum_linuxbridge_agent_packages" => [ "quantum-plugin-linuxbridge", "quantum-plugin-linuxbridge-agent" ], + "quantum_metadata_agent_packages" => [ "quantum-metadata-agent" ], + "quantum_plugin_package" => "quantum-plugin-%plugin%", + "quantum_server_packages" => ["quantum-server"], + "quantum_dhcp_agent_service" => "quantum-dhcp-agent", + "quantum_l3_agent_service" => "quantum-l3-agent", + "quantum_metadata_agent_service" => "quantum-metadata-agent", + "quantum_openvswitch_service" => "openvswitch-switch", + "quantum_openvswitch_agent_service" => "quantum-plugin-openvswitch-agent", + "quantum_linuxbridge_agent_service" => "quantum-plugin-linuxbridge-agent", + "quantum_server_service" => "quantum-server", + "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-network/files/default/disable-eth-offload.sh b/chef/cookbooks/openstack-network/files/default/disable-eth-offload.sh new file mode 100755 index 0000000..2ffad12 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/disable-eth-offload.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2013 ATT Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +### BEGIN INIT INFO +# Provides: disable-eth-offload +# Required-Start: $network +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Disable NIC Offloads +### END INIT INFO + +function check_setting() { + setting_on="false" + INTERFACE=$1 + SETTING=$2 + if [ -z $INTERFACE ] || [ -z $SETTING ]; then + echo "You didn't call check_setting right, it needs interfaces as \$1 and setting as \$2" + exit 1 + fi + + if [ $LOGGING == "true" ]; then + ethtool -k $INTERFACE | grep $SETTING | grep ": on" + fi + + ethtool -k $INTERFACE | grep $SETTING | grep ": on" > /dev/null + if [ $? == 0 ]; then + setting_on="true" + fi +} + +start () { + + INTERFACES=$( grep auto /etc/network/interfaces | grep -v lo | awk '{ print $NF }' ) + declare -A SETTINGS + SETTINGS=( ["lro"]="large-receive-offload" ["tso"]="tcp-segmentation-offload" ["gso"]="generic-segmentation-offload" ["gro"]="generic-receive-offload" ) + ETHTOOL_BIN="/sbin/ethtool" + LOGGING="false" + setting_on="false" + + for interface in $INTERFACES; do + for setting in "${!SETTINGS[@]}"; do + check_setting $interface ${SETTINGS[$setting]} + if [ $setting_on == "true" ]; then + $ETHTOOL_BIN -K $interface $setting off + if [ $LOGGING == "true" ]; then + echo "RUNNING: $ETHTOOL_BIN -K $interface $setting off" + fi + fi + done + done +} + +case $1 in + start) + start + ;; + *) + echo "Usage: $0 {start}" >&2 + exit 1 + ;; +esac + +exit 0 + diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters new file mode 100644 index 0000000..6dbb4d7 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/debug.filters @@ -0,0 +1,14 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# This is needed because we should ping +# from inside a namespace which requires root +ping: RegExpFilter, /bin/ping, root, ping, -w, \d+, -c, \d+, [0-9\.]+ +ping6: RegExpFilter, /bin/ping6, root, ping6, -w, \d+, -c, \d+, [0-9A-Fa-f:]+ diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters new file mode 100644 index 0000000..89abb02 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/dhcp.filters @@ -0,0 +1,40 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# dhcp-agent +ip_exec_dnsmasq: DnsmasqNetnsFilter, /sbin/ip, root +dnsmasq: DnsmasqFilter, /sbin/dnsmasq, root +dnsmasq_usr: DnsmasqFilter, /usr/sbin/dnsmasq, root +# dhcp-agent uses kill as well, that's handled by the generic KillFilter +# it looks like these are the only signals needed, per +# quantum/agent/linux/dhcp.py +kill_dnsmasq: KillFilter, root, /sbin/dnsmasq, -9, -HUP +kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP + +# dhcp-agent uses cat +cat: RegExpFilter, /bin/cat, root, cat, /proc/\d+/cmdline +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root + +# metadata proxy +metadata_proxy: CommandFilter, /usr/bin/quantum-ns-metadata-proxy, root +# If installed from source (say, by devstack), the prefix will be +# /usr/local instead of /usr/bin. +metadata_proxy_local: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root +kill_metadata7: KillFilter, root, /usr/bin/python2.7, -9 +kill_metadata6: KillFilter, root, /usr/bin/python2.6, -9 + +# ip_lib +ip: IpFilter, /sbin/ip, root +ip_usr: IpFilter, /usr/sbin/ip, root +ip_exec: IpNetnsExecFilter, /sbin/ip, root +ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters new file mode 100644 index 0000000..2049e0e --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/iptables-firewall.filters @@ -0,0 +1,21 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# quantum/agent/linux/iptables_manager.py +# "iptables-save", ... +iptables-save: CommandFilter, /sbin/iptables-save, root +iptables-restore: CommandFilter, /sbin/iptables-restore, root +ip6tables-save: CommandFilter, /sbin/ip6tables-save, root +ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root + +# quantum/agent/linux/iptables_manager.py +# "iptables", "-A", ... +iptables: CommandFilter, /sbin/iptables, root +ip6tables: CommandFilter, /sbin/ip6tables, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters new file mode 100644 index 0000000..ec08d59 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/l3.filters @@ -0,0 +1,43 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# arping +arping: CommandFilter, /usr/bin/arping, root +arping_sbin: CommandFilter, /sbin/arping, root + +# l3_agent +sysctl: CommandFilter, /sbin/sysctl, root +route: CommandFilter, /sbin/route, root + +# metadata proxy +metadata_proxy: CommandFilter, /usr/bin/quantum-ns-metadata-proxy, root +# If installed from source (say, by devstack), the prefix will be +# /usr/local instead of /usr/bin. +metadata_proxy_local: CommandFilter, /usr/local/bin/quantum-ns-metadata-proxy, root +kill_metadata7: KillFilter, root, /usr/bin/python2.7, -9 +kill_metadata6: KillFilter, root, /usr/bin/python2.6, -9 + +# ip_lib +ip: IpFilter, /sbin/ip, root +ip_usr: IpFilter, /usr/sbin/ip, root +ip_exec: IpNetnsExecFilter, /sbin/ip, root +ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root + +# ovs_lib (if OVSInterfaceDriver is used) +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root + +# iptables_manager +iptables-save: CommandFilter, /sbin/iptables-save, root +iptables-restore: CommandFilter, /sbin/iptables-restore, root +ip6tables-save: CommandFilter, /sbin/ip6tables-save, root +ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters new file mode 100644 index 0000000..e00a719 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/lbaas-haproxy.filters @@ -0,0 +1,29 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# haproxy +haproxy: CommandFilter, /usr/sbin/haproxy, root + +# lbaas-agent uses kill as well, that's handled by the generic KillFilter +kill_haproxy_usr: KillFilter, root, /usr/sbin/haproxy, -9, -HUP + +# lbaas-agent uses cat +cat: RegExpFilter, /bin/cat, root, cat, /proc/\d+/cmdline + +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root + +# ip_lib +ip: IpFilter, /sbin/ip, root +ip_usr: IpFilter, /usr/sbin/ip, root +ip_exec: IpNetnsExecFilter, /sbin/ip, root +ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters new file mode 100644 index 0000000..301280c --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/linuxbridge-plugin.filters @@ -0,0 +1,21 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# linuxbridge-agent +# unclear whether both variants are necessary, but I'm transliterating +# from the old mechanism +brctl: CommandFilter, /sbin/brctl, root +brctl_usr: CommandFilter, /usr/sbin/brctl, root + +# ip_lib +ip: IpFilter, /sbin/ip, root +ip_usr: IpFilter, /usr/sbin/ip, root +ip_exec: IpNetnsExecFilter, /sbin/ip, root +ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters new file mode 100644 index 0000000..6d8f9c2 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/nec-plugin.filters @@ -0,0 +1,15 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# nec_quantum_agent +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters new file mode 100644 index 0000000..c316448 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/openvswitch-plugin.filters @@ -0,0 +1,29 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# openvswitch-agent +# unclear whether both variants are necessary, but I'm transliterating +# from the old mechanism +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root +ovs-ofctl: CommandFilter, /bin/ovs-ofctl, root +ovs-ofctl_usr: CommandFilter, /usr/bin/ovs-ofctl, root +ovs-ofctl_sbin: CommandFilter, /sbin/ovs-ofctl, root +ovs-ofctl_sbin_usr: CommandFilter, /usr/sbin/ovs-ofctl, root +xe: CommandFilter, /sbin/xe, root +xe_usr: CommandFilter, /usr/sbin/xe, root + +# ip_lib +ip: IpFilter, /sbin/ip, root +ip_usr: IpFilter, /usr/sbin/ip, root +ip_exec: IpNetnsExecFilter, /sbin/ip, root +ip_exec_usr: IpNetnsExecFilter, /usr/sbin/ip, root diff --git a/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters new file mode 100644 index 0000000..696c7d3 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/etc/quantum/rootwrap.d/ryu-plugin.filters @@ -0,0 +1,25 @@ +# quantum-rootwrap command filters for nodes on which quantum is +# expected to control network +# +# This file should be owned by (and only-writeable by) the root user + +# format seems to be +# cmd-name: filter-name, raw-command, user, args + +[Filters] + +# ryu-agent +# unclear whether both variants are necessary, but I'm transliterating +# from the old mechanism + +# quantum/plugins/ryu/agent/ryu_quantum_agent.py: +# "ovs-vsctl", "--timeout=2", ... +ovs-vsctl: CommandFilter, /bin/ovs-vsctl, root +ovs-vsctl_usr: CommandFilter, /usr/bin/ovs-vsctl, root +ovs-vsctl_sbin: CommandFilter, /sbin/ovs-vsctl, root +ovs-vsctl_sbin_usr: CommandFilter, /usr/sbin/ovs-vsctl, root + +# quantum/plugins/ryu/agent/ryu_quantum_agent.py: +# "xe", "vif-param-get", ... +xe: CommandFilter, /bin/xe, root +xe_usr: CommandFilter, /usr/bin/xe, root diff --git a/chef/cookbooks/openstack-network/files/default/openvswitch/install.sh b/chef/cookbooks/openstack-network/files/default/openvswitch/install.sh new file mode 100644 index 0000000..5ec1cd3 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/openvswitch/install.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +modinfo openvswitch |grep 56D59658C3B9FADCD146B12 +if [ $? -eq 0 ]; then + cd /tmp/openvswitch + + yum localinstall -y /tmp/openvswitch/kmod-openvswitch-1.11.0-1.el6.x86_64.rpm + yum localinstall -y /tmp/openvswitch/openvswitch-1.11.0-1.x86_64.rpm + service openvswitch start + modinfo openvswitch + + cd /tmp + rm -rf /tmp/openvswitch +fi + diff --git a/chef/cookbooks/openstack-network/files/default/openvswitch/kmod-openvswitch-1.11.0-1.el6.x86_64.rpm b/chef/cookbooks/openstack-network/files/default/openvswitch/kmod-openvswitch-1.11.0-1.el6.x86_64.rpm new file mode 100644 index 0000000..9274b86 Binary files /dev/null and b/chef/cookbooks/openstack-network/files/default/openvswitch/kmod-openvswitch-1.11.0-1.el6.x86_64.rpm differ diff --git a/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-1.11.0-1.x86_64.rpm b/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-1.11.0-1.x86_64.rpm new file mode 100644 index 0000000..f9c408b Binary files /dev/null and b/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-1.11.0-1.x86_64.rpm differ diff --git a/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-debuginfo-1.11.0-1.x86_64.rpm b/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-debuginfo-1.11.0-1.x86_64.rpm new file mode 100644 index 0000000..c9bccfd Binary files /dev/null and b/chef/cookbooks/openstack-network/files/default/openvswitch/openvswitch-debuginfo-1.11.0-1.x86_64.rpm differ diff --git a/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py b/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py new file mode 100755 index 0000000..55822d3 --- /dev/null +++ b/chef/cookbooks/openstack-network/files/default/quantum-ha-tool.py @@ -0,0 +1,424 @@ +#! /usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 AT&T Services, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import logging +import os +import sys +import json +import argparse +from logging.handlers import SysLogHandler +from collections import OrderedDict +from random import choice +from quantumclient.quantum import client + +LOG = logging.getLogger('quantum-ha-tool') +LOG_FORMAT='%(asctime)s %(name)-12s %(levelname)-8s %(message)s' +LOG_DATE = '%m-%d %H:%M' +DESCRIPTION = "Quantum High Availability Tool" + +def parse_args(): + + # ensure environment has necessary items to authenticate + for key in ['OS_TENANT_NAME', 'OS_USERNAME', 'OS_PASSWORD', + 'OS_AUTH_URL']: + if key not in os.environ.keys(): + LOG.exception("Your environment is missing '%s'") + + ap = argparse.ArgumentParser(description=DESCRIPTION) + ap.add_argument('-d', '--debug', action='store_true', + default=False, help='Show debugging output') + ap.add_argument('-n', '--noop', action='store_true', + default=False, help='Do not do any modifying operations (dry-run)') + ap.add_argument('--l3-agent-check', action='store_true', + default=False, help='Show routers associated with offline l3 agents') + ap.add_argument('--l3-agent-migrate', action='store_true', + default=False, help='Migrate routers away from offline l3 agents') + ap.add_argument('--l3-agent-rebalance', action='store_true', + default=False, help='Rebalance router count on all l3 agents') + ap.add_argument('--replicate-dhcp', action='store_true', + default=False, help='Replicate DHCP configuration to all agents') + return ap.parse_args() + +def setup_logging(args): + level = logging.INFO + if args.debug: + level = logging.DEBUG + logging.basicConfig(level=level, format=LOG_FORMAT, date_fmt=LOG_DATE) + handler = SysLogHandler(address = '/dev/log') + syslog_formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') + handler.setFormatter(syslog_formatter) + LOG.addHandler(handler) + +def run(args): + + # instantiate client + qclient = client.Client('2.0', auth_url=os.environ['OS_AUTH_URL'], + username=os.environ['OS_USERNAME'], + tenant_name=os.environ['OS_TENANT_NAME'], + password=os.environ['OS_PASSWORD']) + + # set json return type + qclient.format = 'json' + + if args.l3_agent_check: + LOG.info("Performing L3 Agent Health Check") + l3_agent_check(qclient, args.noop) + + if args.l3_agent_migrate: + LOG.info("Performing L3 Agent Migration for Offline L3 Agents") + l3_agent_migrate(qclient, args.noop) + + if args.l3_agent_rebalance: + LOG.info("Rebalancing L3 Agent Router Count") + l3_agent_rebalance(qclient, args.noop) + + if args.replicate_dhcp: + LOG.info("Performing DHCP Replication of Networks to Agents") + replicate_dhcp(qclient, args.noop) + +def l3_agent_rebalance(qclient, noop=False): + """ + Rebalance l3 agent router count across agents. The number of routers + on each l3 agent will be as close as possible which should help + distribute load as new l3 agents come online. + + :param qclient: A quantumclient + :param noop: Optional noop flag + """ + + # {u'binary': u'quantum-l3-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, u'topic': + # u'l3_agent', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'L3 agent', u'created_at': u'2013-07-02 14:50:58', u'started_at': u'2013-07-02 18:00:55', + # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', u'configurations': {u'router_id': u'', u'gateway_external_network_id': u'', u'handle_internal_only_routers': True, + # u'use_namespaces': True, u'routers': 5, u'interfaces': 3, u'floating_ips': 9, u'interface_driver': u'quantum.agent.linux.interface.OVSInterfaceDriver', u'ex_gw_ports': 3}}, + + l3_agent_dict={} + agents = list_agents(qclient, agent_type='L3 agent') + num_agents = len(agents) + if num_agents <= 1: + LOG.info("No rebalancing required for 1 or fewer agents") + return + + for l3_agent in agents: + num_routers=l3_agent['configurations']['routers'] + l3_agent_dict[l3_agent['id']] = list_routers_on_l3_agent(qclient, l3_agent['id']) + + ordered_l3_agent_dict = OrderedDict(sorted(l3_agent_dict.items(), key=lambda t: len(t[0]))) + ordered_l3_agent_list = list(ordered_l3_agent_dict) + num_agents = len(ordered_l3_agent_list) + LOG.info("Agent list: %s", ordered_l3_agent_list[0:(num_agents-1/2)+1]) + i=0 + for agent in ordered_l3_agent_list[0:num_agents-1/2]: + low_agent_id=ordered_l3_agent_list[i] + hgh_agent_id=ordered_l3_agent_list[-(i+1)] + + # do nothing if we end up comparing the same router + if low_agent_id == hgh_agent_id: + continue + + LOG.info("Examining low_agent=%s, high_agent=%s", low_agent_id, hgh_agent_id) + + low_agent_router_count = len(l3_agent_dict[low_agent_id]) + hgh_agent_router_count = len(l3_agent_dict[hgh_agent_id]) + + LOG.info("Low Count=%s, High Count=%s", low_agent_router_count, hgh_agent_router_count) + + for router_id in l3_agent_dict[hgh_agent_id]: + if low_agent_router_count >= hgh_agent_router_count: + break + else: + LOG.info("Migrating router=%s from agent=%s to agent=%s", router_id, hgh_agent_id, low_agent_id) + try: + if not noop: + migrate_router(qclient, router_id, hgh_agent_id, low_agent_id) + low_agent_router_count += 1 + hgh_agent_router_count -= 1 + except: + LOG.traceback("Failed to migrate router=%s from agent=%s to agent=%s" % (router_id, hgh_agent_id, low_agent_id)) + continue + i+=1 + +def l3_agent_check(qclient, noop=False): + """ + Walk the l3 agents searching for agents that are offline. Show routers + that are offline and where we would migrate them too. + + :param qclient: A quantumclient + :param noop: Optional noop flag + + """ + + migration_count = 0 + agent_list = list_agents(qclient) + agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') + agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') + LOG.info("There are %s offline L3 agents and %s online L3 agents", len(agent_dead_list), len(agent_alive_list)) + + if len(agent_dead_list) > 0: + + for agent_id in agent_dead_list: + + LOG.info("Querying agent_id=%s for routers to migrate", agent_id) + router_id_list = list_routers_on_l3_agent(qclient, agent_id) + + for router_id in router_id_list: + + try: + target_id = choice(agent_alive_list) + except: + LOG.warn("There are no l3 agents alive we could migrate routers onto") + target_id = None + + LOG.info("Would like to migrate router=%s to agent=%s", router_id, target_id) + +def l3_agent_migrate(qclient, noop=False): + """ + Walk the l3 agents searching for agents that are offline. For those that are + offline, we will retrieve a list of routers on them and migrate them to a + random l3 agent that is online. + + :param qclient: A quantumclient + :param noop: Optional noop flag + + """ + + migration_count = 0 + agent_list = list_agents(qclient) + agent_dead_list = agent_dead_id_list(agent_list, 'L3 agent') + agent_alive_list = agent_alive_id_list(agent_list, 'L3 agent') + LOG.info("There are %s offline L3 agents and %s online L3 agents", len(agent_dead_list), len(agent_alive_list)) + + if len(agent_dead_list) > 0: + + if len(agent_alive_list) < 1: + LOG.exception("There are no l3 agents alive to migrate routers onto") + + for agent_id in agent_dead_list: + + LOG.info("Querying agent_id=%s for routers to migrate", agent_id) + router_id_list = list_routers_on_l3_agent(qclient, agent_id) + + for router_id in router_id_list: + + target_id = choice(agent_alive_list) + LOG.info("Migrating router=%s to agent=%s", router_id, target_id) + router_body = {'router_id': router_id} + + try: + + if not noop: + migrate_router(qclient, router_id, agent_id, target_id) + migration_count+=1 + + except: + + LOG.exception("There was an error migrating a router") + continue + + LOG.info("%s routers required migration from offline L3 agents", migration_count) + +def replicate_dhcp(qclient, noop=False): + """ + Retrieve a network list and then probe each DHCP agent to ensure they have that + network assigned. + + :param qclient: A quantumclient + :param noop: Optional noop flag + """ + + added=0 + networks = list_networks(qclient) + network_id_list = [n['id'] for n in networks] + agents = list_agents(qclient, agent_type='DHCP agent') + LOG.info("Replicating %s networks to %s DHCP agents", len(networks), len(agents)) + for dhcp_agent_id in [a['id'] for a in agents]: + networks_on_agent = qclient.list_networks_on_dhcp_agent(dhcp_agent_id)['networks'] + network_id_on_agent = [n['id'] for n in networks_on_agent] + for network_id in network_id_list: + if network_id not in network_id_on_agent: + try: + dhcp_body = {'network_id': network_id} + if not noop: + qclient.add_network_to_dhcp_agent(dhcp_agent_id, dhcp_body) + LOG.info("Added missing network=%s to dhcp agent=%s", network_id, dhcp_agent_id) + added+=1 + except: + LOG.exception("Failed to add network_id=%s to dhcp_agent=%s", network_id, dhcp_agent_id) + continue + + LOG.info("Added %s networks to DHCP agents", added) + + +def migrate_router(qclient, router_id, agent_id, target_id): + """ + Returns nothing, and raises on exception + + :param qclient: A quantumclient + :param router_id: The id of the router to migrate + :param agent_id: The id of the l3 agent to migrate from + :param target_id: The id of the l3 agent to migrate to + """ + + # N.B. The quantum API will return "success" even when there is a subsequent + # failure during the add or remove process so we must check to ensure the + # router has been added or removed + + # remove the router from the dead agent + qclient.remove_router_from_l3_agent(agent_id, router_id) + + # ensure it is removed or log an error + if router_id in list_routers_on_l3_agent(qclient, agent_id): + LOG.exception("Failed to remove router_id=%s from agent_id=%s", router_id, agent_id) + + + # add the router id to a live agent + router_body = {'router_id': router_id} + qclient.add_router_to_l3_agent(target_id, router_body) + + # ensure it is removed or log an error + if router_id not in list_routers_on_l3_agent(qclient, target_id): + LOG.exception("Failed to add router_id=%s from agent_id=%s", router_id, agent_id) + + +def list_networks(qclient): + """ + Return a list of network objects + + :param qclient: A quantumclient + """ + + resp = qclient.list_networks() + LOG.debug("list_networks: %s", resp) + return resp['networks'] + +def list_dhcp_agent_networks(qclient, agent_id): + """ + Return a list of network ids assigned to a particular DHCP agent + + :param qclient: A quantumclient + :param agent_id: A DHCP agent id + """ + + resp = qclient.list_networks_on_dhcp_agent(agent_id) + LOG.debug("list_networks_on_dhcp_agent: %s", resp) + return [s['id'] for s in resp['networks']] + + + +def list_routers(qclient): + """ + Return a list of router objects + + :param qclient: A quantumclient + + # {'routers': [{u'status': u'ACTIVE', u'external_gateway_info': {u'network_id': u'b970297c-d80e-4527-86d7-e49d2da9fdef'}, u'name': u'router1', + # u'admin_state_up': True, u'tenant_id': u'5603b97ee7f047ea999e25492c7fcb23', u'routes': [], u'id': u'0a122e5c-1623-412e-8c53-a1e21d1daff8'}, + + """ + + resp = qclient.list_routers() + LOG.debug("list_routers: %s", resp) + return resp['routers'] + +def list_routers_on_l3_agent(qclient, agent_id): + """ + Return a list of router ids on an agent + + :param qclient: A quantumclient + """ + + resp = qclient.list_routers_on_l3_agent(agent_id) + LOG.debug("list_routers_on_l3_agent: %s", resp) + return [r['id'] for r in resp['routers']] + +def list_agents(qclient, agent_type=None): + """ + Return a list of agent objects + + :param qclient: A quantumclient + + # openvswitch + # + # {u'agents': [{u'binary': u'quantum-openvswitch-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:25' + # u'alive': True, u'topic': u'N/A', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'Open vSwitch agent', u'created_at': u'2013-07-02 14:50:57', + # u'started_at': u'2013-07-02 14:50:57', u'id': u'3a577f1d-d86e-4f1a-a395-8d4c8e4df1e2', u'configurations': {u'devices': 10}}, + # + # dhcp + # + # {u'binary': u'quantum-dhcp-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, + # u'topic': u'dhcp_agent', u'host': u'o5r4.int.san3.attcompute.com', u'agent_type': u'DHCP agent', u'created_at': u'2013-06-26 16:21:02', u'started_at': + # u'2013-06-28 13:32:52', u'id': u'3e8be28e-05a0-472b-9288-a59f8d8d2271', u'configurations': {u'subnets': 4, u'use_namespaces': True, u'dhcp_driver': + # u'quantum.agent.linux.dhcp.Dnsmasq', u'networks': 4, u'dhcp_lease_time': 120, u'ports': 38}}, + # + # l3 + # + # {u'binary': u'quantum-l3-agent', u'description': None, u'admin_state_up': True, u'heartbeat_timestamp': u'2013-07-02 22:20:23', u'alive': True, u'topic': + # u'l3_agent', u'host': u'o3r3.int.san3.attcompute.com', u'agent_type': u'L3 agent', u'created_at': u'2013-07-02 14:50:58', u'started_at': u'2013-07-02 18:00:55', + # u'id': u'6efe494a-616c-41ea-9c8f-2c592f4d46ff', u'configurations': {u'router_id': u'', u'gateway_external_network_id': u'', u'handle_internal_only_routers': True, + # u'use_namespaces': True, u'routers': 5, u'interfaces': 3, u'floating_ips': 9, u'interface_driver': u'quantum.agent.linux.interface.OVSInterfaceDriver', u'ex_gw_ports': 3}}, + """ + + resp = qclient.list_agents() + LOG.debug("list_agents: %s", resp) + if agent_type: + filtered=[] + for agent in resp['agents']: + if agent['agent_type'] == agent_type: + filtered.append(agent) + return filtered + return resp['agents'] + +def agent_alive_id_list(agent_list, agent_type): + """ + Return a list of agents that are alive from an API list of agents + + :param agent_list: API response for list_agents() + + """ + live_list=[] + for agent in agent_list: + if agent['agent_type'] == agent_type and agent['alive'] is True: + live_list.append(agent['id']) + return live_list + +def agent_dead_id_list(agent_list, agent_type): + """ + Return a list of agents that are dead from an API list of agents + + :param agent_list: API response for list_agents() + + """ + dead_list=[] + for agent in agent_list: + if agent['agent_type'] == agent_type and agent['alive'] is False: + dead_list.append(agent['id']) + return dead_list + +if __name__ == '__main__': + + args = parse_args() + setup_logging(args) + + try: + run(args) + sys.exit(0) + except Exception as err: + print "ERROR: %s" % err + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) diff --git a/chef/cookbooks/openstack-network/metadata.rb b/chef/cookbooks/openstack-network/metadata.rb new file mode 100644 index 0000000..2cd3ab5 --- /dev/null +++ b/chef/cookbooks/openstack-network/metadata.rb @@ -0,0 +1,19 @@ +name "openstack-network" +maintainer "Jay Pipes " +license "Apache 2.0" +description "Installs and configures the OpenStack Network API Service and various agents and plugins" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "7.0.0" +recipe "openstack-network::server", "Installs packages required for a OpenStack Network server" +recipe "openstack-network::openvswitch", "Installs packages required for OVS" +recipe "openstack-network::metadata_agent", "Installs packages required for a OpenStack Network Metadata Agent" +recipe "openstack-network::identity_registration", "Registers OpenStack Network endpoints and service user with Keystone" + +%w{ ubuntu fedora redhat centos suse }.each do |os| + supports os +end + +depends "openstack-identity", "~> 7.0" +depends "openstack-common", "~> 0.4.0" +depends "mysql" +depends "postgresql" diff --git a/chef/cookbooks/openstack-network/recipes/balancer.rb b/chef/cookbooks/openstack-network/recipes/balancer.rb new file mode 100644 index 0000000..e0b0601 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/balancer.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: balancer +# +# Copyright 2013, Mirantis IT +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This recipe should be placed in the run_list of the node that +# runs the network server or network controller server. + +platform_options = node["openstack"]["network"]["platform"] + +service "quantum-server" do + service_name platform_options["quantum_server_service"] + supports :status => true, :restart => true + + action :nothing +end + +platform_options["quantum_lb_packages"].each do |pkg| + package pkg do + action :install + end +end + +directory node["openstack"]["network"]["lbaas_config_path"] do + action :create + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + recursive true +end + +template "#{node["openstack"]["network"]["lbaas_config_path"]}/lbaas_agent.ini" do + source "lbaas_agent.ini.erb" + notifies :restart, "service[quantum-server]", :immediately +end diff --git a/chef/cookbooks/openstack-network/recipes/bigswitch.rb b/chef/cookbooks/openstack-network/recipes/bigswitch.rb new file mode 100644 index 0000000..4e387b2 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/bigswitch.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: bigswitch +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/brocade.rb b/chef/cookbooks/openstack-network/recipes/brocade.rb new file mode 100644 index 0000000..8c191ce --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/brocade.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: brocade +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/cisco.rb b/chef/cookbooks/openstack-network/recipes/cisco.rb new file mode 100644 index 0000000..17a8c9b --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/cisco.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: cisco +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/common.rb b/chef/cookbooks/openstack-network/recipes/common.rb new file mode 100644 index 0000000..a006843 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/common.rb @@ -0,0 +1,407 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: common +# +# Copyright 2013, AT&T +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +platform_options = node["openstack"]["network"]["platform"] + +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +core_plugin = node["openstack"]["network"]["core_plugin"] + +if node["openstack"]["network"]["syslog"]["use"] + include_recipe "openstack-common::logging" +end + +platform_options["nova_network_packages"].each do |pkg| + package pkg do + action :purge + end +end + +platform_options["quantum_packages"].each do |pkg| + package pkg do + action :install + end +end + +directory "/etc/quantum/plugins" do + recursive true + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00700 + action :create +end + +directory "/var/cache/quantum" do + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00700 + action :create +end + +directory ::File.dirname node["openstack"]["network"]["api"]["auth"]["cache_dir"] do + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00700 + + only_if { node["openstack"]["auth"]["strategy"] == "pki" } +end + +# This will copy recursively all the files in +# /files/default/etc/quantum/rootwrap.d +remote_directory "/etc/quantum/rootwrap.d" do + source "etc/quantum/rootwrap.d" + files_owner node["openstack"]["network"]["platform"]["user"] + files_group node["openstack"]["network"]["platform"]["group"] + files_mode 00700 +end + +template "/etc/quantum/rootwrap.conf" do + source "rootwrap.conf.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 +end + +template "/etc/quantum/policy.json" do + source "policy.json.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + + notifies :restart, "service[quantum-server]", :delayed +end + +# Add by Sam, CentOS think Qpid as it's default queue, it will auto add qpid +# configuration at /etc/quantum/quantum.conf when invoke quantum-xxx-setup, +# because these scripts was hard coded a line as below: +# "openstack-config --set ${Q_CONF} DEFAULT +# rpc_backend quantum.openstack.common.rpc.impl_qpid" +# It should be a bug of redhat OS +execute "delete_auto_qpid" do + command %Q|sed -i "s/^rpc_backend = quantum.openstack.common.rpc.impl_qpid//g" /etc/quantum/quantum.conf| + only_if { + (node['openstack']['mq']['service_type'] == "rabbitmq") and + platform?(%w{fedora centos redhat}) + } + action :nothing +end + +rabbit_server_role = node["openstack"]["network"]["rabbit_server_chef_role"] +if node["openstack"]["network"]["rabbit"]["ha"] + rabbit_hosts = rabbit_servers +end +rabbit_pass = user_password node['openstack']['mq']['password'] + +identity_endpoint = endpoint "identity-api" +auth_uri = ::URI.decode identity_endpoint.to_s + +db_user = node['openstack']['db']['network']['username'] +db_pass = db_password node['openstack']['db']['network']['password'] +sql_connection = db_uri("network", db_user, db_pass) + +api_endpoint = endpoint "network-api" +service_pass = service_password node['openstack']['identity']['network']['password'] +service_tenant_name = node['openstack']['identity']['network']['tenant'] +service_user = node['openstack']['identity']['network']['username'] + +if node["openstack"]["network"]["api"]["bind_interface"].nil? + bind_address = api_endpoint.host + bind_port = api_endpoint.port +else + bind_address = address_for node["openstack"]["network"]["api"]["bind_interface"] + #bind_address = node['openstack']['endpoints']['network-api']['host'] + bind_port = node["openstack"]["network"]["api"]["bind_port"] +end + +# retrieve the local interface for tunnels +if node["openstack"]["network"]["openvswitch"]["local_ip_interface"].nil? + local_ip = localhost +else + local_ip = node["openstack"]["network"]["openvswitch"]["local_ip"] +end + +platform_options["quantum_client_packages"].each do |pkg| + package pkg do + action :upgrade + options platform_options["package_overrides"] + end +end + +# all recipes include common.rb, and some servers +# may just be running a subset of agents (like l3_agent) +# and not the api server components, so we ignore restart +# failures here as there may be no quantum-server process +service "quantum-server" do + service_name platform_options["quantum_server_service"] + supports :status => true, :restart => true + ignore_failure true + + action :nothing +end + +template "/etc/quantum/quantum.conf" do + source "quantum.conf.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :bind_address => bind_address, + :bind_port => bind_port, + :rabbit_hosts => rabbit_hosts, + :rabbit_pass => rabbit_pass, + :core_plugin => core_plugin, + :identity_endpoint => identity_endpoint, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + + notifies :restart, "service[quantum-server]", :delayed +end + + +template "/etc/quantum/api-paste.ini" do + source "api-paste.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + "identity_endpoint" => identity_endpoint, + "service_tenant_name" => service_tenant_name, + "service_user" => service_user, + "service_pass" => service_pass + ) + + notifies :restart, "service[quantum-server]", :delayed +end + +directory "/etc/quantum/plugins/#{main_plugin}" do + recursive true + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00700 +end + +# For several plugins, the plugin configuration +# is required by both the quantum-server and +# ancillary services that may be on different +# physical servers like the l3 agent, so we assume +# the plugin configuration is a "common" file + +template_file = nil + +case main_plugin +when "bigswitch" + + template_file = "/etc/quantum/plugins/bigswitch/restproxy.ini" + template "/etc/quantum/plugins/bigswitch/restproxy.ini" do + source "plugins/bigswitch/restproxy.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "brocade" + + template_file = "/etc/quantum/plugins/brocade/brocade.ini" + template "/etc/quantum/plugins/brocade/brocade.ini" do + source "plugins/brocade/brocade.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "cisco" + + template_file = "/etc/quantum/plugins/cisco/cisco_plugins.ini" + template "/etc/quantum/plugins/cisco/cisco_plugins.ini" do + source "plugins/cisco/cisco_plugins.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "hyperv" + + template_file = "/etc/quantum/plugins/hyperv/hyperv_quantum_plugin.ini.erb" + template "/etc/quantum/plugins/hyperv/hyperv_quantum_plugin.ini.erb" do + source "plugins/hyperv/hyperv_quantum_plugin.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "linuxbridge" + + template_file = "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" + template "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" do + source "plugins/linuxbridge/linuxbridge_conf.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "midonet" + + template_file = "/etc/quantum/plugins/metaplugin/metaplugin.ini" + template "/etc/quantum/plugins/metaplugin/metaplugin.ini" do + source "plugins/metaplugin/metaplugin.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "nec" + + template_file = "/etc/quantum/plugins/nec/nec.ini" + template "/etc/quantum/plugins/nec/nec.ini" do + source "plugins/nec/nec.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "nicira" + + template_file = "/etc/quantum/plugins/nicira/nvp.ini" + template "/etc/quantum/plugins/nicira/nvp.ini" do + source "plugins/nicira/nvp.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "openvswitch" + + template_file = "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" + + service "quantum-plugin-openvswitch-agent" do + service_name platform_options["quantum_openvswitch_agent_service"] + action :nothing + end + + template "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" do + source "plugins/openvswitch/ovs_quantum_plugin.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection, + :local_ip => local_ip + ) + notifies :restart, "service[quantum-server]", :delayed + if node.run_list.expand(node.chef_environment).recipes.include?("openstack-network::openvswitch") + notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + end + end + + +when "plumgrid" + + template_file = "/etc/quantum/plugins/plumgrid/plumgrid.ini" + template "/etc/quantum/plugins/plumgrid/plumgrid.ini" do + source "plugins/plumgrid/plumgrid.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +when "ryu" + + template_file = "/etc/quantum/plugins/ryu/ryu.ini" + template "/etc/quantum/plugins/ryu/ryu.ini" do + source "plugins/ryu/ryu.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :sql_connection => sql_connection + ) + + notifies :restart, "service[quantum-server]", :delayed + end + +end + + +template "/etc/default/quantum-server" do + source "quantum-server.erb" + owner "root" + group "root" + mode 00644 + variables( + :plugin_config => template_file + ) + only_if { + node.run_list.expand(node.chef_environment).recipes.include?("openstack-network::server") + platform?(%w{ubuntu debian}) + } +end diff --git a/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb b/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb new file mode 100644 index 0000000..b087346 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/dhcp_agent.rb @@ -0,0 +1,132 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: dhcp_agent +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] + +platform_options["quantum_dhcp_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :install + end +end + +service "quantum-dhcp-agent" do + service_name platform_options["quantum_dhcp_agent_service"] + supports :status => true, :restart => true + + action :enable +end + +# Some plugins have DHCP functionality, so we install the plugin +# Python package and include the plugin-specific recipe here... +package platform_options["quantum_plugin_package"].gsub("%plugin%", main_plugin) do + options platform_options["package_overrides"] + action :install + # plugins are installed by the main openstack-quantum package on SUSE + not_if { platform_family? "suse" } +end + +execute "quantum-dhcp-setup --plugin #{main_plugin}" do + notifies :run, "execute[delete_auto_qpid]", :immediately + only_if { platform?(%w(fedora redhat centos)) } # :pragma-foodcritic: ~FC024 - won't fix this +end + +template "/etc/quantum/dnsmasq.conf" do + source "dnsmasq.conf.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + notifies :restart, "service[quantum-dhcp-agent]", :delayed +end + +template "/etc/quantum/dhcp_agent.ini" do + source "dhcp_agent.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + notifies :restart, "service[quantum-dhcp-agent]", :immediately +end + +# Deal with ubuntu precise dnsmasq 2.59 version by custom +# compiling a more recent version of dnsmasq +# +# See: +# https://lists.launchpad.net/openstack/msg11696.html +# https://bugs.launchpad.net/ubuntu/+source/dnsmasq/+bug/1013529 +# https://bugs.launchpad.net/ubuntu/+source/dnsmasq/+bug/1103357 +# http://www.thekelleys.org.uk/dnsmasq/CHANGELOG (SO_BINDTODEVICE) +# +# Would prefer a PPA or backport but there are none and upstream +# has no plans to fix +if node['lsb'] && node['lsb']['codename'] == "precise" + + platform_options["quantum_dhcp_build_packages"].each do |pkg| + package pkg do + action :install + end + end + + dhcp_options = node['openstack']['network']['dhcp'] + + src_filename = dhcp_options['dnsmasq_filename'] + src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}" + extract_path = "#{Chef::Config['file_cache_path']}/#{dhcp_options['dnsmasq_checksum']}" + + remote_file src_filepath do + source dhcp_options['dnsmasq_url'] + checksum dhcp_options['dnsmasq_checksum'] + owner 'root' + group 'root' + mode 00644 + end + + bash 'extract_package' do + cwd ::File.dirname(src_filepath) + code <<-EOH + mkdir -p #{extract_path} + tar xzf #{src_filename} -C #{extract_path} + mv #{extract_path}/*/* #{extract_path}/ + cd #{extract_path}/ + echo '2.65' > VERSION + debian/rules binary + EOH + not_if { ::File.exists?(extract_path) } + notifies :install, "dpkg_package[dnsmasq-utils]", :immediately + notifies :install, "dpkg_package[dnsmasq-base]", :immediately + notifies :install, "dpkg_package[dnsmasq]", :immediately + end + + dpkg_package "dnsmasq-utils" do + source "#{extract_path}/../dnsmasq-utils_#{dhcp_options['dnsmasq_dpkgversion']}_#{dhcp_options['dnsmasq_architecture']}.deb" + action :nothing + end + dpkg_package "dnsmasq-base" do + source "#{extract_path}/../dnsmasq-base_#{dhcp_options['dnsmasq_dpkgversion']}_#{dhcp_options['dnsmasq_architecture']}.deb" + action :nothing + end + dpkg_package "dnsmasq" do + source "#{extract_path}/../dnsmasq_#{dhcp_options['dnsmasq_dpkgversion']}_all.deb" + action :nothing + end + +end diff --git a/chef/cookbooks/openstack-network/recipes/hyperv.rb b/chef/cookbooks/openstack-network/recipes/hyperv.rb new file mode 100644 index 0000000..dff4b5e --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/hyperv.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: hyperv +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/identity_registration.rb b/chef/cookbooks/openstack-network/recipes/identity_registration.rb new file mode 100644 index 0000000..fdb727d --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/identity_registration.rb @@ -0,0 +1,88 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: identity_registration +# +# Copyright 2013, AT&T +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" + +bootstrap_token = secret "secrets", "#{node['openstack']['identity']['admin_token']}" +auth_uri = ::URI.decode identity_admin_endpoint.to_s + +api_endpoint = endpoint "network-api" + +service_pass = service_password "openstack-network" +service_tenant_name = node["openstack"]["network"]["service_tenant_name"] +service_user = node["openstack"]["network"]["service_user"] +service_role = node["openstack"]["network"]["service_role"] + +openstack_identity_register "Register Network API Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name node["openstack"]["network"]["service_name"] + service_type node["openstack"]["network"]["service_type"] + service_description "OpenStack Network Service" + + action :create_service +end + +openstack_identity_register "Register Network Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type node["openstack"]["network"]["service_type"] + endpoint_region node["openstack"]["network"]["region"] + endpoint_adminurl api_endpoint.to_s + endpoint_internalurl api_endpoint.to_s + endpoint_publicurl api_endpoint.to_s + + action :create_endpoint +end + +openstack_identity_register "Register Service Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + tenant_description "Service Tenant" + + action :create_tenant +end + +openstack_identity_register "Register #{service_user} User" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + user_pass service_pass + + action :create_user +end + +openstack_identity_register "Grant '#{service_role}' Role to #{service_user} User for #{service_tenant_name} Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name service_tenant_name + user_name service_user + role_name service_role + + action :grant_role +end diff --git a/chef/cookbooks/openstack-network/recipes/l3_agent.rb b/chef/cookbooks/openstack-network/recipes/l3_agent.rb new file mode 100644 index 0000000..b921988 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/l3_agent.rb @@ -0,0 +1,68 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: l3_agent +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] + +# Sam added the if case, cannot find the independent l3-agent packages at centos 6.4 +platform_options["quantum_l3_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :install + # The providers below do not use the generic L3 agent... + not_if { ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) } + end +end + +service "quantum-l3-agent" do + service_name platform_options["quantum_l3_agent_service"] + supports :status => true, :restart => true + + action :enable +end + +execute "quantum-l3-setup --plugin #{main_plugin}" do + notifies :run, "execute[delete_auto_qpid]", :immediately + only_if { + platform?(%w(fedora redhat centos)) and not # :pragma-foodcritic: ~FC024 - won't fix this + ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) + } +end + +template "/etc/quantum/l3_agent.ini" do + source "l3_agent.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + notifies :restart, "service[quantum-l3-agent]", :immediately +end + +if not ["nicira", "plumgrid", "bigswitch", "linuxbridge"].include?(main_plugin) + # See http://docs.openstack.org/trunk/openstack-network/admin/content/install_quantum-l3.html + ext_bridge = node["openstack"]["network"]["l3"]["external_network_bridge"] + ext_bridge_iface = node["openstack"]["network"]["l3"]["external_network_bridge_interface"] + execute "create external network bridge" do + command "ovs-vsctl add-br #{ext_bridge} && ovs-vsctl add-port #{ext_bridge} #{ext_bridge_iface}" + action :run + not_if "ovs-vsctl show | grep 'Bridge #{ext_bridge}'" + end +end diff --git a/chef/cookbooks/openstack-network/recipes/linuxbridge.rb b/chef/cookbooks/openstack-network/recipes/linuxbridge.rb new file mode 100644 index 0000000..d34df30 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/linuxbridge.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: linuxbridge +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] + +platform_options["quantum_linuxbridge_agent_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :install + end +end + +service "quantum-plugin-linuxbridge-agent" do + service_name platform_options["quantum_linuxbridge_agent_service"] + supports :status => true, :restart => true + action :enable +end diff --git a/chef/cookbooks/openstack-network/recipes/metadata_agent.rb b/chef/cookbooks/openstack-network/recipes/metadata_agent.rb new file mode 100644 index 0000000..1c83aaf --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/metadata_agent.rb @@ -0,0 +1,59 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: metadata_agent +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] + +identity_endpoint = endpoint "identity-api" +service_tenant_name = node['openstack']['identity']['network']['tenant'] +service_user = node['openstack']['identity']['network']['username'] +service_pass = service_password node['openstack']['identity']['network']['password'] +metadata_secret = secret "secrets", node["openstack"]["network"]["metadata"]["secret_name"] + +template "/etc/quantum/metadata_agent.ini" do + source "metadata_agent.ini.erb" + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00644 + variables( + :identity_endpoint => identity_endpoint, + :metadata_secret => metadata_secret, + :service_tenant_name => service_tenant_name, + :service_user => service_user, + :service_pass => service_pass + ) + notifies :restart, "service[quantum-metadata-agent]", :immediately + action :create +end + +platform_options["quantum_metadata_agent_packages"].each do |pkg| + package pkg do + action :install + options platform_options["package_overrides"] + end +end + +service "quantum-metadata-agent" do + service_name platform_options["quantum_metadata_agent_service"] + supports :status => true, :restart => true + action :enable +end diff --git a/chef/cookbooks/openstack-network/recipes/metaplugin.rb b/chef/cookbooks/openstack-network/recipes/metaplugin.rb new file mode 100644 index 0000000..2351012 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/metaplugin.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: metaplugin +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/midonet.rb b/chef/cookbooks/openstack-network/recipes/midonet.rb new file mode 100644 index 0000000..fd12cc3 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/midonet.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: midonet +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/nec.rb b/chef/cookbooks/openstack-network/recipes/nec.rb new file mode 100644 index 0000000..89a8cd0 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/nec.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: nec +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/nicira.rb b/chef/cookbooks/openstack-network/recipes/nicira.rb new file mode 100644 index 0000000..abe667e --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/nicira.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: nicira +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/openvswitch.rb b/chef/cookbooks/openstack-network/recipes/openvswitch.rb new file mode 100644 index 0000000..31bc267 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/openvswitch.rb @@ -0,0 +1,182 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: opensvswitch +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'uri' + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +core_plugin = node["openstack"]["network"]["core_plugin"] + +if platform?("ubuntu", "debian") + + # obtain kernel version for kernel header + # installation on ubuntu and debian + kernel_ver = node["kernel"]["release"] + package "linux-headers-#{kernel_ver}" do + options platform_options["package_overrides"] + action :install + end + +end + +directory "/var/run/openvswitch" do + recursive true + owner node["openstack"]["network"]["platform"]["user"] + group node["openstack"]["network"]["platform"]["group"] + mode 00755 + action :create +end + + +platform_options["quantum_openvswitch_packages"].each do |pkg| + package pkg do + action :install + end +end + +# The current openvswitch package of centos 6.4 cannot create GRE tunnel successfully +# The centos 6.4 kernel version is 2.6.32-358.18.1.el6.x86_64 +if platform?(%w(fedora redhat centos)) + remote_directory "/tmp/openvswitch" do + source "openvswitch" + files_owner "root" + files_group "root" + mode "0644" + recursive true + action :create + end + + execute "update openvswitch package" do + ignore_failure true + command "chmod +x /tmp/openvswitch/install.sh; sh /tmp/openvswitch/install.sh" + action :run + end +end + +service "quantum-server" do + service_name node["openstack"]["network"]["platform"]["quantum_server_service"] + supports :status => true, :restart => true + action :nothing +end + +service "quantum-openvswitch-switch" do + service_name platform_options["quantum_openvswitch_service"] + supports :status => true, :restart => true + action :restart +end + + +service "quantum-server" do + service_name platform_options["quantum_server_service"] + supports :status => true, :restart => true + ignore_failure true + action :nothing +end + +platform_options["quantum_openvswitch_agent_packages"].each do |pkg| + package pkg do + action :install + options platform_options["package_overrides"] + end +end + +service "quantum-plugin-openvswitch-agent" do + service_name platform_options["quantum_openvswitch_agent_service"] + supports :status => true, :restart => true + action [ :enable, :restart ] +end + +execute "chkconfig openvswitch on" do + only_if { platform?(%w(fedora redhat centos)) } +end + +execute "quantum-node-setup --plugin openvswitch" do + only_if { platform?(%w(fedora redhat centos)) } # :pragma-foodcritic: ~FC024 - won't fix this + notifies :run, "execute[delete_auto_qpid]", :immediately +end + +if not ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) + int_bridge = node["openstack"]["network"]["openvswitch"]["integration_bridge"] + execute "create internal network bridge" do + ignore_failure true + command "ovs-vsctl add-br #{int_bridge}" + action :run + not_if "ovs-vsctl show | grep 'Bridge #{int_bridge}'" + notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + end +end + +if not ["nicira", "plumgrid", "bigswitch"].include?(main_plugin) + if node["openstack"]["network"]["openvswitch"]["tenant_network_type"] == 'gre' + tun_bridge = node["openstack"]["network"]["openvswitch"]["tunnel_bridge"] + execute "create tunnel network bridge" do + ignore_failure true + command "ovs-vsctl add-br #{tun_bridge}" + action :run + not_if "ovs-vsctl show | grep '#{tun_bridge}'" + notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + end + end + + if node["openstack"]["network"]["openvswitch"]["tenant_network_type"] == 'vlan' + ethernet=node['openstack']['networking']['tenant']['interface'] + bridge_mappings = node["openstack"]["network"]["openvswitch"]["bridge_mappings"] + bridge = bridge_mappings.split(":").map(&:strip).reject(&:empty?)[1] + execute "create tunnel network bridge" do + ignore_failure true + command "ovs-vsctl add-br #{bridge};ovs-vsctl add-port #{bridge} #{ethernet}" + action :run + not_if "ovs-vsctl show | grep '#{bridge}'" + notifies :restart, "service[quantum-plugin-openvswitch-agent]", :delayed + end + end +end + +if node['openstack']['network']['disable_offload'] + + package "ethtool" do + action :install + options platform_options["package_overrides"] + end + + service "disable-eth-offload" do + supports :restart => false, :start => true, :stop => false, :reload => false + priority({ 2 => [ :start, 19 ]}) + action :nothing + end + + # a priority of 19 ensures we start before openvswitch + # at least on ubuntu and debian + cookbook_file "disable-eth-offload-script" do + path "/etc/init.d/disable-eth-offload" + source "disable-eth-offload.sh" + owner "root" + group "root" + mode "0755" + notifies :enable, "service[disable-eth-offload]" + notifies :start, "service[disable-eth-offload]" + end +end diff --git a/chef/cookbooks/openstack-network/recipes/plumgrid.rb b/chef/cookbooks/openstack-network/recipes/plumgrid.rb new file mode 100644 index 0000000..bd715ae --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/plumgrid.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: plumgrid +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/ryu.rb b/chef/cookbooks/openstack-network/recipes/ryu.rb new file mode 100644 index 0000000..0be3e54 --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/ryu.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: ryu +# +# Copyright 2013, AT&T +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-network::common" diff --git a/chef/cookbooks/openstack-network/recipes/server.rb b/chef/cookbooks/openstack-network/recipes/server.rb new file mode 100644 index 0000000..427818e --- /dev/null +++ b/chef/cookbooks/openstack-network/recipes/server.rb @@ -0,0 +1,108 @@ +# +# Cookbook Name:: openstack-network +# Recipe:: server +# +# Copyright 2013, AT&T +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +include_recipe "openstack-network::common" + +platform_options = node["openstack"]["network"]["platform"] +driver_name = node["openstack"]["network"]["interface_driver"].split('.').last.downcase +main_plugin = node["openstack"]["network"]["interface_driver_map"][driver_name] +core_plugin = node["openstack"]["network"]["core_plugin"] + +platform_options = node["openstack"]["network"]["platform"] + +platform_options["quantum_server_packages"].each do |pkg| + package pkg do + options platform_options["package_overrides"] + action :install + end +end + +platform_options["quantum_openvswitch_agent_packages"].each do|pkg| + package pkg do + options platform_options["package_overrides"] + action :install + end +end + +if node['platform_family'] == "rhel" + directory "/var/cache/quantum/api" do + owner node['openstack']['services']['network']['name'] + group node['openstack']['services']['network']['name'] + recursive true + end + + template "/etc/init.d/quantum-server" do + source "quantum-server.start.erb" + owner "root" + group "root" + mode 00755 + end +end + +service "quantum-server" do + service_name platform_options["quantum_server_service"] + supports :status => true, :restart => true + action [ :enable, :restart ] +end + +cookbook_file "quantum-ha-tool" do + source "quantum-ha-tool.py" + path node["openstack"]["network"]["quantum_ha_cmd"] + owner "root" + group "root" + mode 00755 +end + +if node["openstack"]["network"]["quantum_ha_cmd_cron"] + # ensure period checks are offset between multiple l3 agent nodes + # and assumes splay will remain constant (i.e. based on hostname) + # Generate a uniformly distributed unique number to sleep. + checksum = Digest::MD5.hexdigest(node['fqdn'] || 'unknown-hostname') + splay = node['chef_client']['splay'].to_i || 3000 + sleep_time = checksum.to_s.hex % splay + + cron "quantum-ha-healthcheck" do + minute node["openstack"]["network"]["cron_l3_healthcheck"] + command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["quantum_ha_cmd"]} --l3-agent-migrate > /dev/null 2>&1" + end + + cron "quantum-ha-replicate-dhcp" do + minute node["openstack"]["network"]["cron_replicate_dhcp"] + command "sleep #{sleep_time} ; . /root/openrc && #{node["openstack"]["network"]["quantum_ha_cmd"]} --replicate-dhcp > /dev/null 2>&1" + end +end + +# the default SUSE initfile uses this sysconfig file to determine the +# quantum plugin to use +template "/etc/sysconfig/quantum" do + only_if { platform? "suse" } + source "quantum.sysconfig.erb" + owner "root" + group "root" + mode 00644 + variables( + :plugin_conf => node["openstack"]["network"]["plugin_conf_map"][driver_name] + ) + notifies :restart, "service[quantum-server]" +end diff --git a/chef/cookbooks/openstack-network/spec/balancer_spec.rb b/chef/cookbooks/openstack-network/spec/balancer_spec.rb new file mode 100644 index 0000000..750e732 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/balancer_spec.rb @@ -0,0 +1,32 @@ +require_relative 'spec_helper' + +describe 'openstack-network::balancer' do + + describe "ubuntu" do + + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::balancer" + end + + ['haproxy', 'quantum-lbaas-agent'].each do |pack| + it "installs #{pack} package" do + expect(@chef_run).to install_package pack + end + end + + it 'creates directory /etc/quantum/plugins/services/agent_loadbalancer' do + expect(@chef_run).to create_directory '/etc/quantum/plugins/services/agent_loadbalancer' + end + + it 'balancer config' do + configf = "/etc/quantum/plugins/services/agent_loadbalancer/lbaas_agent.ini" + expect(@chef_run).to create_file configf + expect(@chef_run).to create_file_with_content configf, /periodic_interval = 10/ + expect(@chef_run).to create_file_with_content configf, /interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver/ + end + + end + +end diff --git a/chef/cookbooks/openstack-network/spec/common_spec.rb b/chef/cookbooks/openstack-network/spec/common_spec.rb new file mode 100644 index 0000000..3190d97 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/common_spec.rb @@ -0,0 +1,19 @@ +require_relative 'spec_helper' + +describe "openstack-network::common" do + describe "ubuntu" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::common" + end + + it "upgrades python quantumclient" do + expect(@chef_run).to upgrade_package "python-quantumclient" + end + + it "upgrades python pyparsing" do + expect(@chef_run).to upgrade_package "python-pyparsing" + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb new file mode 100644 index 0000000..fb43e38 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/dhcp_agent-opensuse_spec.rb @@ -0,0 +1,37 @@ +require_relative 'spec_helper' + +describe 'openstack-network::dhcp_agent' do + + describe "opensuse" do + + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS + @chef_run.converge "openstack-network::dhcp_agent" + end + + it "installs quamtum dhcp package" do + expect(@chef_run).to install_package "openstack-quantum-dhcp-agent" + end + + it "installs plugin packages" do + expect(@chef_run).not_to install_package(/openvswitch/) + expect(@chef_run).not_to install_package(/plugin/) + end + + it "starts the dhcp agent on boot" do + expect(@chef_run).to( + set_service_to_start_on_boot "openstack-quantum-dhcp-agent") + end + + it "/etc/quantum/dhcp_agent.ini has the proper owner" do + expect(@chef_run.template "/etc/quantum/dhcp_agent.ini").to( + be_owned_by "openstack-quantum", "openstack-quantum") + end + + it "/etc/quantum/dnsmasq.conf has the proper owner" do + expect(@chef_run.template "/etc/quantum/dnsmasq.conf").to( + be_owned_by "openstack-quantum", "openstack-quantum") + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb b/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb new file mode 100644 index 0000000..61ed837 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/dhcp_agent_spec.rb @@ -0,0 +1,91 @@ +require_relative 'spec_helper' + +describe 'openstack-network::dhcp_agent' do + + describe "ubuntu" do + + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::dhcp_agent" + end + + # since our mocked version of ubuntu is precise, our compile + # utilities should be installed to build dnsmasq + it "installs dnsmasq build dependencies" do + [ "build-essential", "pkg-config", "libidn11-dev", "libdbus-1-dev", "libnetfilter-conntrack-dev", "gettext" ].each do |pkg| + expect(@chef_run).to install_package pkg + end + end + + it "installs quamtum dhcp package" do + expect(@chef_run).to install_package "quantum-dhcp-agent" + end + + it "installs plugin packages" do + expect(@chef_run).to install_package "quantum-plugin-openvswitch" + end + + it "starts the dhcp agent on boot" do + expect(@chef_run).to set_service_to_start_on_boot "quantum-dhcp-agent" + end + + describe "/etc/quantum/plugins" do + before do + @file = @chef_run.directory "/etc/quantum/plugins" + end + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "700" + end + end + + describe "/etc/quantum/dhcp_agent.ini" do + before do + @file = @chef_run.template "/etc/quantum/dhcp_agent.ini" + end + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + it "uses ovs driver" do + expect(@chef_run).to create_file_with_content @file.name, + "interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver" + end + it "uses namespaces" do + expect(@chef_run).to create_file_with_content @file.name, + "use_namespaces = True" + end + it "checks dhcp domain" do + expect(@chef_run).to create_file_with_content @file.name, + /^dhcp_domain = openstacklocal$/ + end + end + + describe "/etc/quantum/dnsmasq.conf" do + before do + @file = @chef_run.template "/etc/quantum/dnsmasq.conf" + end + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + it "overrides dhcp options" do + expect(@chef_run).to create_file_with_content @file.name, + "dhcp-option=26,1454" + end + it "checks upstream resolvers" do + expect(@chef_run).to create_file_with_content @file.name, + /^server=209.244.0.3$/ + expect(@chef_run).to create_file_with_content @file.name, + /^server=8.8.8.8$/ + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb b/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb new file mode 100644 index 0000000..920a370 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/identity_registration_spec.rb @@ -0,0 +1,89 @@ +require_relative "spec_helper" + +describe "openstack-network::identity_registration" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::identity_registration" + end + + it "registers network service" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Network API Service" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "network", + :service_description => "OpenStack Network Service", + :action => [:create_service] + ) + end + + it "registers network endpoint" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Network Endpoint" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :service_type => "network", + :endpoint_region => "RegionOne", + :endpoint_adminurl => "http://127.0.0.1:9696", + :endpoint_internalurl => "http://127.0.0.1:9696", + :endpoint_publicurl => "http://127.0.0.1:9696", + :action => [:create_endpoint] + ) + end + + it "registers service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register Service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :tenant_description => "Service Tenant", + :action => [:create_tenant] + ) + end + + it "registers service user" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Register quantum User" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :user_name => "quantum", + :user_pass => "quantum-pass", + :action => [:create_user] + ) + end + + it "grants admin role to service user for service tenant" do + resource = @chef_run.find_resource( + "openstack-identity_register", + "Grant 'admin' Role to quantum User for service Tenant" + ).to_hash + + expect(resource).to include( + :auth_uri => "http://127.0.0.1:35357/v2.0", + :bootstrap_token => "bootstrap-token", + :tenant_name => "service", + :role_name => "admin", + :user_name => "quantum", + :action => [:grant_role] + ) + end +end diff --git a/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb b/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb new file mode 100644 index 0000000..7fbc18f --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/l3_agent_spec.rb @@ -0,0 +1,52 @@ +require_relative 'spec_helper' + +describe 'openstack-network::l3_agent' do + + describe "ubuntu" do + + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::l3_agent" + end + + it "installs quamtum l3 package" do + expect(@chef_run).to install_package "quantum-l3-agent" + end + + describe "l3_agent.ini" do + + before do + @file = @chef_run.template "/etc/quantum/l3_agent.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "it has ovs driver" do + expect(@chef_run).to create_file_with_content @file.name, + "interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver" + end + + it "sets fuzzy delay to default" do + expect(@chef_run).to create_file_with_content @file.name, + "periodic_fuzzy_delay = 5" + end + + it "it does not set a nil router_id" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^router_id =/ + end + + it "it does not set a nil router_id" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^gateway_external_network_id =/ + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb new file mode 100644 index 0000000..f8bf4db --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/linuxbridge-opensuse_spec.rb @@ -0,0 +1,23 @@ +require_relative 'spec_helper' + +describe 'openstack-network::linuxbridge' do + + describe "opensuse" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" + end + @chef_run.converge "openstack-network::linuxbridge" + end + + it "installs linuxbridge agent" do + expect(@chef_run).to install_package "openstack-quantum-linuxbridge-agent" + end + + it "sets the linuxbridge service to start on boot" do + expect(@chef_run).to set_service_to_start_on_boot "openstack-quantum-linuxbridge-agent" + end + + end +end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb new file mode 100644 index 0000000..5fde89f --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/linuxbridge-redhat_spec.rb @@ -0,0 +1,23 @@ +require_relative 'spec_helper' + +describe 'openstack-network::linuxbridge' do + + describe "redhat" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS do |n| + n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" + end + @chef_run.converge "openstack-network::linuxbridge" + end + + it "installs linuxbridge agent" do + expect(@chef_run).to install_package "openstack-quantum-linuxbridge" + end + + it "sets the linuxbridge service to start on boot" do + expect(@chef_run).to set_service_to_start_on_boot "quantum-linuxbridge-agent" + end + + end +end diff --git a/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb b/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb new file mode 100644 index 0000000..d2efb05 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/linuxbridge_spec.rb @@ -0,0 +1,42 @@ +require_relative 'spec_helper' + +describe 'openstack-network::linuxbridge' do + + describe "ubuntu" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" + end + @chef_run.converge "openstack-network::linuxbridge" + end + + it "installs linuxbridge agent" do + expect(@chef_run).to install_package "quantum-plugin-linuxbridge-agent" + end + + it "sets the linuxbridge service to start on boot" do + expect(@chef_run).to set_service_to_start_on_boot "quantum-plugin-linuxbridge-agent" + end + + describe "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" do + before do + @file = @chef_run.template( + "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini") + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has a correct sql_connection value" do + expect(@chef_run).to create_file_with_content( + @file.name, "mysql://quantum:quantum-pass@127.0.0.1:3306/quantum") + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb b/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb new file mode 100644 index 0000000..2c08975 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/metadata_agent_spec.rb @@ -0,0 +1,65 @@ +require_relative 'spec_helper' + +describe 'openstack-network::metadata_agent' do + + describe "ubuntu" do + + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-network::metadata_agent" + end + + it "installs quamtum metadata agent" do + expect(@chef_run).to install_package "quantum-metadata-agent" + end + + describe "metadata_agent.ini" do + + before do + @file = @chef_run.template "/etc/quantum/metadata_agent.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "sets auth url correctly" do + expect(@chef_run).to create_file_with_content @file.name, + "auth_url = http://127.0.0.1:5000/v2.0" + end + it "sets auth region correctly" do + expect(@chef_run).to create_file_with_content @file.name, + "auth_region = RegionOne" + end + it "sets admin tenant name" do + expect(@chef_run).to create_file_with_content @file.name, + "admin_tenant_name = service" + end + it "sets admin user" do + expect(@chef_run).to create_file_with_content @file.name, + "admin_user = quantum" + end + it "sets admin password" do + expect(@chef_run).to create_file_with_content @file.name, + "admin_password = quantum-pass" + end + it "sets nova metadata ip correctly" do + expect(@chef_run).to create_file_with_content @file.name, + "nova_metadata_ip = 127.0.0.1" + end + it "sets nova metadata ip correctly" do + expect(@chef_run).to create_file_with_content @file.name, + "nova_metadata_port = 8775" + end + it "sets quantum secret correctly" do + expect(@chef_run).to create_file_with_content @file.name, + "metadata_proxy_shared_secret = metadata-secret" + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb new file mode 100644 index 0000000..8bef084 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/openvswitch-opensuse_spec.rb @@ -0,0 +1,26 @@ +require_relative "spec_helper" + +describe 'openstack-network::server' do + describe "opensuse" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["chef_client"]["splay"] = 300 + end + @node = @chef_run.node + @chef_run.converge "openstack-network::openvswitch" + end + + it "installs the openvswitch package" do + expect(@chef_run).to install_package "openvswitch-switch" + end + + it "installs the openvswitch-agent package" do + expect(@chef_run).to install_package "openstack-quantum-openvswitch-agent" + end + + it "starts the openvswitch-switch service" do + expect(@chef_run).to set_service_to_start_on_boot "openvswitch-switch" + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb b/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb new file mode 100644 index 0000000..2cde58f --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/openvswitch_spec.rb @@ -0,0 +1,82 @@ +require_relative 'spec_helper' + +describe 'openstack-network::openvswitch' do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.automatic_attrs["kernel"]["release"] = "1.2.3" + n.set["openstack"]["network"]["local_ip_interface"] = "eth0" + end + @chef_run.converge "openstack-network::openvswitch" + end + + it "installs openvswitch switch" do + expect(@chef_run).to install_package "openvswitch-switch" + end + it "installs openvswitch datapath dkms" do + expect(@chef_run).to install_package "openvswitch-datapath-dkms" + end + it "installs linux bridge utils" do + expect(@chef_run).to install_package "bridge-utils" + end + it "installs linux linux headers" do + expect(@chef_run).to install_package "linux-headers-1.2.3" + end + it "sets the openvswitch service to start on boot" do + expect(@chef_run).to set_service_to_start_on_boot 'openvswitch-switch' + end + it "installs openvswitch agent" do + expect(@chef_run).to install_package "quantum-plugin-openvswitch-agent" + end + it "sets the openvswitch service to start on boot" do + expect(@chef_run).to set_service_to_start_on_boot "quantum-plugin-openvswitch-agent" + end + + describe "ovs_quantum_plugin.ini" do + + before do + @file = @chef_run.template "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "uses default network_vlan_range" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^network_vlan_ranges =/ + end + it "uses default tunnel_id_ranges" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^tunnel_id_ranges =/ + end + it "uses default integration_bridge" do + expect(@chef_run).to create_file_with_content @file.name, + "integration_bridge = br-int" + end + it "uses default tunnel bridge" do + expect(@chef_run).to create_file_with_content @file.name, + "tunnel_bridge = br-tun" + end + it "uses default int_peer_patch_port" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^int_peer_patch_port =/ + end + it "uses default tun_peer_patch_port" do + expect(@chef_run).not_to create_file_with_content @file.name, + /^tun_peer_patch_port =/ + end + it "it has firewall driver" do + expect(@chef_run).to create_file_with_content @file.name, + "firewall_driver = quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver" + end + it "it uses local_ip from eth0 when local_ip_interface is set" do + expect(@chef_run).to create_file_with_content @file.name, + "local_ip = 10.0.0.3" + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb b/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb new file mode 100644 index 0000000..b08d564 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/server-opensuse_spec.rb @@ -0,0 +1,63 @@ +require_relative "spec_helper" + +describe 'openstack-network::server' do + describe "opensuse" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["chef_client"]["splay"] = 300 + end + @node = @chef_run.node + @chef_run.converge "openstack-network::server" + end + + it "installs openstack-quantum packages" do + expect(@chef_run).to install_package "openstack-quantum" + end + + it "enables openstack-quantum service" do + expect(@chef_run).to enable_service "openstack-quantum" + end + + it "does not install openvswitch package" do + opts = ::OPENSUSE_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts do |n| + n.set["chef_client"]["splay"] = 300 + end + chef_run.converge "openstack-network::server" + + expect(chef_run).not_to install_package "openstack-quantum-openvswitch" + end + + describe "/etc/sysconfig/quantum" do + before do + @file = @chef_run.template("/etc/sysconfig/quantum") + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has the correct plugin config location - ovs by default" do + expect(@chef_run).to create_file_with_content( + @file.name, "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini") + end + + it "uses linuxbridge when configured to use it" do + chef_run = ::ChefSpec::ChefRunner.new ::OPENSUSE_OPTS do |n| + n.set["openstack"]["network"]["interface_driver"] = "quantum.agent.linux.interface.BridgeInterfaceDriver" + end + chef_run.converge "openstack-network::server" + + expect(chef_run).to create_file_with_content( + "/etc/sysconfig/quantum", + "/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini" + ) + end + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb b/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb new file mode 100644 index 0000000..31cced4 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/server-redhat_spec.rb @@ -0,0 +1,28 @@ +require_relative "spec_helper" + +describe 'openstack-network::server' do + describe "redhat" do + before do + quantum_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::REDHAT_OPTS + @node = @chef_run.node + @chef_run.converge "openstack-network::server" + end + + it "installs openstack-quantum packages" do + expect(@chef_run).to install_package "openstack-quantum" + end + + it "enables openstack-quantum server service" do + expect(@chef_run).to enable_service "quantum-server" + end + + it "does not install openvswitch package" do + opts = ::REDHAT_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-network::server" + expect(chef_run).not_to install_package "openvswitch" + expect(chef_run).not_to enable_service "openstack-quantum-openvswitch-agent" + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/server_spec.rb b/chef/cookbooks/openstack-network/spec/server_spec.rb new file mode 100644 index 0000000..659068a --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/server_spec.rb @@ -0,0 +1,208 @@ +require_relative 'spec_helper' + +describe 'openstack-network::server' do + before { quantum_stubs } + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS do |n| + n.set["openstack"]["mq"] = { + "host" => "127.0.0.1" + } + n.set["chef_client"]["splay"] = 300 + end + @chef_run.converge "openstack-network::server" + end + + describe "package and services" do + + it "installs quantum packages" do + expect(@chef_run).to install_package "quantum-server" + end + + it "starts server service" do + expect(@chef_run).to enable_service "quantum-server" + end + + it "does not install openvswitch package or the agent" do + expect(@chef_run).not_to install_package "openvswitch" + expect(@chef_run).not_to install_package "quantum-plugin-openvswitch-agent" + expect(@chef_run).not_to enable_service "quantum-plugin-openvswitch-agent" + end + + end + + describe "api-paste.ini" do + + before do + @file = @chef_run.template "/etc/quantum/api-paste.ini" + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has quantum pass" do + expect(@chef_run).to create_file_with_content @file.name, + "admin_password = quantum-pass" + end + + end + + it "should create quantum-ha-tool.py script" do + expect(@chef_run).to create_cookbook_file "/usr/local/bin/quantum-ha-tool.py" + end + + describe "quantum.conf" do + + before do + @file = @chef_run.template "/etc/quantum/quantum.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "quantum", "quantum" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "it sets root_helper" do + expect(@chef_run).to create_file_with_content @file.name, + 'root_helper = "sudo quantum-rootwrap /etc/quantum/rootwrap.conf"' + end + + it "binds to appropriate api ip" do + expect(@chef_run).to create_file_with_content @file.name, + "bind_host = 127.0.0.1" + end + + it "binds to appropriate api port" do + expect(@chef_run).to create_file_with_content @file.name, + "bind_port = 9696" + end + + it "has appropriate auth host for agents" do + expect(@chef_run).to create_file_with_content @file.name, + "auth_host = 127.0.0.1" + end + + it "has appropriate auth port for agents" do + expect(@chef_run).to create_file_with_content @file.name, + "auth_port = 5000" + end + + it "has appropriate admin password for agents" do + expect(@chef_run).to create_file_with_content @file.name, + "admin_password = quantum-pass" + end + + it "has rabbit_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_hosts" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_hosts=" + end + + it "does not have rabbit_ha_queues" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_ha_queues=" + end + + it "has rabbit_port" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_port=5672" + end + + it "has rabbit_userid" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_userid=guest" + end + + it "has rabbit_password" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_password=rabbit-pass" + end + + it "has rabbit_virtual_host" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_virtual_host=/" + end + + it "it does not allow overlapping ips by default" do + expect(@chef_run).to create_file_with_content @file.name, + "allow_overlapping_ips = False" + end + + it "it has correct default scheduler classes" do + expect(@chef_run).to create_file_with_content @file.name, + "network_scheduler_driver = quantum.scheduler.dhcp_agent_scheduler.ChanceScheduler" + expect(@chef_run).to create_file_with_content @file.name, + "router_scheduler_driver = quantum.scheduler.l3_agent_scheduler.ChanceScheduler" + end + + describe "quantum.conf with rabbit ha" do + + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["network"]["rabbit"]["ha"] = true + n.set["chef_client"]["splay"] = 300 + end + @chef_run.converge "openstack-network::server" + end + + it "has rabbit_hosts" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_hosts=1.1.1.1:5672,2.2.2.2:5672" + end + + it "has rabbit_ha_queues" do + expect(@chef_run).to create_file_with_content @file.name, + "rabbit_ha_queues=True" + end + + it "does not have rabbit_host" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_host=127.0.0.1" + end + + it "does not have rabbit_port" do + expect(@chef_run).not_to create_file_with_content @file.name, + "rabbit_port=5672" + end + end + + describe "/etc/default/quantum-server" do + before do + @file = @chef_run.template( + "/etc/default/quantum-server") + end + + it "has proper owner" do + expect(@file).to be_owned_by "root", "root" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "has a correct plugin config path" do + expect(@chef_run).to create_file_with_content( + @file.name, "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini") + end + end + + it "does not install sysconfig template" do + chef_run = ::ChefSpec::ChefRunner.new( + ::UBUNTU_OPTS.merge(:evaluate_guards => true)) + chef_run.stub_command(/python/, true) + chef_run.converge "openstack-network::server" + expect(chef_run).not_to create_file "/etc/sysconfig/quantum" + end + end +end diff --git a/chef/cookbooks/openstack-network/spec/spec_helper.rb b/chef/cookbooks/openstack-network/spec/spec_helper.rb new file mode 100644 index 0000000..2cec4e0 --- /dev/null +++ b/chef/cookbooks/openstack-network/spec/spec_helper.rb @@ -0,0 +1,69 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::OPENSUSE_OPTS = { + :platform => "opensuse", + :version => "12.3", + :log_level => ::LOG_LEVEL +} +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +MOCK_NODE_NETWORK_DATA = + { + "ipaddress" => '10.0.0.2', + "fqdn" => 'localhost.localdomain', + "hostname" => 'localhost', + "network" => { + "default_interface" => "eth0", + "interfaces" => { + "eth0" => { + "addresses" => { + "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"}, + "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"}, + "08:00:27:CA:AB:08" => {"family" => "lladdr"} + }, + }, + "lo" => { + "addresses" => { + "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"}, + "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"} + }, + }, + }, + } + } + +def quantum_stubs + + ::Chef::Recipe.any_instance.stub(:rabbit_servers). + and_return "1.1.1.1:5672,2.2.2.2:5672" + ::Chef::Recipe.any_instance.stub(:config_by_role). + with("rabbitmq-server", "queue").and_return( + {'host' => 'rabbit-host', 'port' => 'rabbit-port'} + ) + ::Chef::Recipe.any_instance.stub(:config_by_role). + with("glance-api", "glance").and_return [] + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "openstack_identity_bootstrap_token"). + and_return "bootstrap-token" + ::Chef::Recipe.any_instance.stub(:db_password).and_return "quantum-pass" + ::Chef::Recipe.any_instance.stub(:secret). + with("secrets", "quantum_metadata_secret"). + and_return "metadata-secret" + ::Chef::Recipe.any_instance.stub(:user_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:service_password).and_return String.new + ::Chef::Recipe.any_instance.stub(:service_password).with("openstack-network"). + and_return "quantum-pass" + ::Chef::Recipe.any_instance.stub(:user_password).with("guest"). + and_return("rabbit-password") + +end diff --git a/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb b/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb new file mode 100644 index 0000000..5899fcc --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/api-paste.ini.erb @@ -0,0 +1,34 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[composite:quantum] +use = egg:Paste#urlmap +/: quantumversions +/v2.0: quantumapi_v2_0 + +[composite:quantumapi_v2_0] +use = call:quantum.auth:pipeline_factory +noauth = extensions quantumapiapp_v2_0 +keystone = authtoken keystonecontext extensions quantumapiapp_v2_0 + +[filter:keystonecontext] +paste.filter_factory = quantum.auth:QuantumKeystoneContext.factory + +[filter:extensions] +paste.filter_factory = quantum.api.extensions:plugin_aware_extension_middleware_factory + +[app:quantumversions] +paste.app_factory = quantum.api.versions:Versions.factory + +[app:quantumapiapp_v2_0] +paste.app_factory = quantum.api.v2.router:APIRouter.factory + +[filter:authtoken] +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory +auth_host = <%= @identity_endpoint.host %> +auth_port = <%= @identity_endpoint.port %> +auth_protocol = <%= @identity_endpoint.scheme %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%=@service_user %> +admin_password = <%= @service_pass %> +delay_auth_decision = true +signing_dir = <%= node["openstack"]["network"]["api"]["auth"]["cache_dir"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb new file mode 100644 index 0000000..202ec1f --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/dhcp_agent.ini.erb @@ -0,0 +1,55 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[DEFAULT] +# Show debugging output in log (sets DEBUG log level output) +debug = <%= node["openstack"]["network"]["debug"] %> + +# The DHCP agent will resync its state with Quantum to recover from any +# transient notification or rpc errors. The interval is number of +# seconds between attempts. +resync_interval = <%= node["openstack"]["network"]["dhcp"]["resync_interval"] %> + +# The DHCP requires that an inteface driver be set. Choose the one that best +# matches your plugin. + +# OVS based plugins (OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) +# interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +# LinuxBridge +#interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +interface_driver = <%= node["openstack"]["network"]["interface_driver"] %> + +# OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS +# as OpenFlow switch and check port status +ovs_use_veth = <%= node["openstack"]["network"]["dhcp"]["ovs_use_veth"] %> + +# The agent can use other DHCP drivers. Dnsmasq is the simplest and requires +# no additional setup of the DHCP server. +dhcp_driver = <%= node["openstack"]["network"]["dhcp_driver"] %> + +# Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and +# iproute2 package that supports namespaces). +use_namespaces = <%= node["openstack"]["network"]["use_namespaces"] %> + +# The DHCP server can assist with providing metadata support on isolated +# networks. Setting this value to True will cause the DHCP server to append +# specific host routes to the DHCP request. The metadata service will only +# be activated when the subnet gateway_ip is None. The guest instance must +# be configured to request host routes via DHCP (Option 121). +enable_isolated_metadata = <%= node["openstack"]["network"]["dhcp"]["enable_isolated_metadata"] %> + +# Allows for serving metadata requests coming from a dedicated metadata +# access network whose cidr is 169.254.169.254/16 (or larger prefix), and +# is connected to a Quantum router from which the VMs send metadata +# request. In this case DHCP Option 121 will not be injected in VMs, as +# they will be able to reach 169.254.169.254 through a router. +# This option requires enable_isolated_metadata = True +enable_metadata_network = <%= node["openstack"]["network"]["dhcp"]["enable_metadata_network"] %> + +# Domain to use for building the host names of instances. +# If not set, it will default to "openstacklocal" +dhcp_domain = <%= node["openstack"]["network"]["dhcp"]["default_domain"] %> + +# Pass a config file to dnsmasq so we can override settings +# like the mtu passed to the virtual machine +dnsmasq_config_file = /etc/quantum/dnsmasq.conf + diff --git a/chef/cookbooks/openstack-network/templates/default/dnsmasq.conf.erb b/chef/cookbooks/openstack-network/templates/default/dnsmasq.conf.erb new file mode 100644 index 0000000..0f42252 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/dnsmasq.conf.erb @@ -0,0 +1,7 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +dhcp-option=<%= node["openstack"]["network"]["dhcp"]["dhcp-option"] %> + +<% node["openstack"]["network"]["dhcp"]["upstream_dns_servers"].each do |dns_server| -%> +server=<%= dns_server %> +<% end -%> diff --git a/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb new file mode 100644 index 0000000..da9edfa --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/l3_agent.ini.erb @@ -0,0 +1,58 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[DEFAULT] +# Show debugging output in log (sets DEBUG log level output) +debug = <%= node["openstack"]["network"]["debug"] %> + +# L3 requires that an interface driver be set. Choose the one that best +# matches your plugin. + +# OVS based plugins (OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) +# interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +# LinuxBridge +# interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +interface_driver = <%= node["openstack"]["network"]["interface_driver"] %> + +# Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and +# iproute2 package that supports namespaces). +use_namespaces = <%= node["openstack"]["network"]["use_namespaces"] %> + +# If use_namespaces is set as False then the agent can only configure one router. +# This is done by setting the specific router_id. +# Default: router_id = +<% if node["openstack"]["network"]["l3"]["router_id"] -%> +router_id = <%= node["openstack"]["network"]["l3"]["router_id"] %> +<% end -%> + +# Each L3 agent can be associated with at most one external network. This +# value should be set to the UUID of that external network. If empty, +# the agent will enforce that only a single external networks exists and +# use that external network id +# Default: gateway_external_network_id = +<% if node["openstack"]["network"]["l3"]["gateway_external_network_id"] -%> +gateway_external_network_id = <%= node["openstack"]["network"]["l3"]["gateway_external_network_id"] %> +<% end -%> + +# Indicates that this L3 agent should also handle routers that do not have +# an external network gateway configured. This option should be True only +# for a single agent in a Quantum deployment, and may be False for all agents +# if all routers must have an external network gateway +handle_internal_only_routers = <%= node["openstack"]["network"]["l3"]["handle_internal_only_routers"] %> + +# Name of bridge used for external network traffic. This should be set to +# empty value for the linux bridge +external_network_bridge = <%= node["openstack"]["network"]["l3"]["external_network_bridge"] %> + +# TCP Port used by Quantum metadata server +metadata_port = <%= node["openstack"]["network"]["l3"]["metadata_port"] %> + +# Send this many gratuitous ARPs for HA setup. Set it below or equal to 0 +# to disable this feature. +send_arp_for_ha = <%= node["openstack"]["network"]["l3"]["send_arp_for_ha"] %> + +# seconds between re-sync routers' data if needed +periodic_interval = <%= node["openstack"]["network"]["l3"]["periodic_interval"] %> + +# seconds to start to sync routers' data after +# starting agent +periodic_fuzzy_delay = <%= node["openstack"]["network"]["l3"]["periodic_fuzzy_delay"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb new file mode 100644 index 0000000..b6e3913 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/lbaas_agent.ini.erb @@ -0,0 +1,37 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[DEFAULT] +# Show debugging output in log (sets DEBUG log level output) +debug = <%= node["openstack"]["network"]["debug"] %> + +# The LBaaS agent will resync its state with Quantum to recover from any +# transient notification or rpc errors. The interval is number of +# seconds between attempts. +periodic_interval = <%= node["openstack"]["network"]["lbaas"]["periodic_interval"] %> + +<% case node["openstack"]["network"]["lbaas_plugin"] +when "ovs" %> +# OVS based plugins(OVS, Ryu, NEC, NVP, BigSwitch/Floodlight) +interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver +# OVS based plugins(Ryu, NEC, NVP, BigSwitch/Floodlight) that use OVS +# as OpenFlow switch and check port status +#ovs_use_veth = True +<% when "linuxbridge" %> +# LinuxBridge +interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver +<% else %> +# LBaaS currently supports openvswitch and linuxbridge drivers only. +# Please use one of them. +interface_driver = +<% end %> + +# The agent requires a driver to manage the loadbalancer. HAProxy is the +# opensource version. +device_driver = quantum.plugins.services.agent_loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver + +# Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and +# iproute2 package that supports namespaces). +# use_namespaces = True + +# The user group +# user_group = nogroup diff --git a/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb b/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb new file mode 100644 index 0000000..429be08 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/metadata_agent.ini.erb @@ -0,0 +1,30 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[DEFAULT] +# Show debugging output in log (sets DEBUG log level output) +debug = <%= node["openstack"]["network"]["debug"] %> + +# The Quantum user information for accessing the Quantum API. +auth_url = <%= @identity_endpoint.to_s %> +auth_region = <%= node["openstack"]["network"]["region"] %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> + +# IP address used by Nova metadata server +# Default: nova_metadata_ip = 127.0.0.1 +<% if node["openstack"]["network"]["metadata"]["nova_metadata_ip"] -%> +nova_metadata_ip = <%= node["openstack"]["network"]["metadata"]["nova_metadata_ip"] %> +<% end -%> + +# TCP Port used by Nova metadata server +# Default: nova_metadata_port = 8775 +<% if node["openstack"]["network"]["metadata"]["nova_metadata_port"] -%> +nova_metadata_port = <%= node["openstack"]["network"]["metadata"]["nova_metadata_port"] %> +<% end -%> + +# When proxying metadata requests, Quantum signs the Instance-ID header with a +# shared secret to prevent spoofing. You may select any string for a secret, +# but it must match here and in the configuration used by the Nova Metadata +# Server. NOTE: Nova uses a different key: quantum_metadata_proxy_shared_secret +metadata_proxy_shared_secret = <%= @metadata_secret %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb new file mode 100644 index 0000000..708f184 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/bigswitch/restproxy.ini.erb @@ -0,0 +1,39 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:pass@127.0.0.1:3306/restproxy_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[RESTPROXY] +# All configuration for this plugin is in section '[restproxy]' +# +# The following parameters are supported: +# servers : [,]* (Error if not set) +# server_auth : (default: no auth) +# server_ssl : True | False (default: False) +# sync_data : True | False (default: False) +# server_timeout : 10 (default: 10 seconds) +# +servers = <%= node["openstack"]["network"]["bigswitch"]["servers"] %> +#server_auth=username:password +#server_ssl=True +#sync_data=True +#server_timeout=10 diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb new file mode 100644 index 0000000..cc0f4b3 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/brocade/brocade.ini.erb @@ -0,0 +1,57 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[SWITCH] +# username = +username = <%= node["openstack"]["network"]["brocade"]["switch_username"] %> +# password = +password = <%= node["openstack"]["network"]["brocade"]["switch_password"] %> +# address = +address = <%= node["openstack"]["network"]["brocade"]["switch_address"] %> +# ostype = NOS +ostype = <%= node["openstack"]["network"]["brocade"]["switch_ostype"] %> + +# Example: +# username = admin +# password = password +# address = 10.24.84.38 +# ostype = NOS + +[DATABASE] +# sql_connection = sqlite:// +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 +# +# Example: +# sql_connection = mysql://root:pass@localhost/brcd_quantum?charset=utf8 +sql_connection = <%= @sql_connection %> + +[PHYSICAL_INTERFACE] +# physical_interface = +# +# Example: +# physical_interface = physnet1 +physical_interface = <%= node["openstack"]["network"]["brocade"]["physical_interface"] %> + +[VLANS] +# network_vlan_ranges = :nnnn:mmmm +# +# Example: +# network_vlan_ranges = physnet1:1000:2999 +network_vlan_ranges = <%= node["openstack"]["network"]["brocade"]["network_vlan_ranges"] %> + +[AGENT] +# Example: +# root_helper = sudo /usr/local/bin/quantum-rootwrap /etc/quantum/rootwrap.conf + +[LINUX_BRIDGE] +# physical_interface_mappings = : +# +# Example: +# physical_interface_mappings = physnet1:em1 +physical_interface_mappings = <%= node["openstack"]["network"]["brocade"]["physical_interface_mappings"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb new file mode 100644 index 0000000..656d9de --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/cisco/cisco_plugins.ini.erb @@ -0,0 +1,36 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[CISCO_PLUGINS] +nexus_plugin = <%= node["openstack"]["network"]["cisco"]["nexus_plugin"] %> +vswitch_plugin = <%= node["openstack"]["network"]["cisco"]["vswitch_plugin"] %> + +[CISCO] +vlan_start = <%= node["openstack"]["network"]["cisco"]["vlan_start"] %> +vlan_end = <%= node["openstack"]["network"]["cisco"]["vlan_end"] %> +vlan_name_prefix = <%= node["openstack"]["network"]["cisco"]["vlan_name_prefix"] %> +max_ports = <%= node["openstack"]["network"]["cisco"]["max_ports"] %> +max_port_profiles = <%= node["openstack"]["network"]["cisco"]["max_port_profiles"] %> +max_networks = <%= node["openstack"]["network"]["cisco"]["max_networks"] %> +model_class = <%= node["openstack"]["network"]["cisco"]["model_class"] %> +manager_class = <%= node["openstack"]["network"]["cisco"]["manager_class"] %> +nexus_driver = <%= node["openstack"]["network"]["cisco"]["nexus_driver"] %> + +<%- node["openstack"]["network"]["cisco"]["nexus_switch"].each_pair do | ip, info | -%> +[NEXUS_SWITCH:<%= @ip %>] +<%- @info["hosts"].each do | host_info | -%> +<%= @host_info[0] %> = <%= @host_info[1] %> +<%- end -%> +ssh_port = <%= @info["ssh_port"] %> +username = <%= @info["username"] %> +password = <%= @info["password"] %> + +<%- end -%> + +[DATABASE] +# +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://quantum:password@127.0.0.1:3306/cisco_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +# +sql_connection = <%= @sql_connection %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb new file mode 100644 index 0000000..947e048 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/hyperv/hyperv_quantum_plugin.ini.erb @@ -0,0 +1,68 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://quantum:password@127.0.0.1:3306/hyperv_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[HYPERV] +# (StrOpt) Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST either change this +# to 'vlan' and configure network_vlan_ranges below or to 'flat'. +# Set to 'none' to disable creation of tenant networks. +# +# Default: tenant_network_type = local +# Example: tenant_network_type = vlan +tenant_network_type = <%= node["openstack"]["network"]["hyperv"]["tenant_network_type"] %> + +# (ListOpt) Comma-separated list of +# [::] tuples enumerating ranges +# of VLAN IDs on named physical networks that are available for +# allocation. All physical networks listed are available for flat and +# VLAN provider network creation. Specified ranges of VLAN IDs are +# available for tenant network allocation if tenant_network_type is +# 'vlan'. If empty, only gre and local networks may be created. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 +network_vlan_ranges = <%= node["openstack"]["network"]["hyperv"]["network_vlan_ranges"] %> + +[AGENT] +# Agent's polling interval in seconds +polling_interval = <%= node["openstack"]["network"]["hyperv"]["polling_interval"] %> + +# (ListOpt) Comma separated list of : +# where the physical networks can be expressed with wildcards, +# e.g.: ."*:external". +# The referred external virtual switches need to be already present on +# the Hyper-V server. +# If a given physical network name will not match any value in the list +# the plugin will look for a virtual switch with the same name. +# +# Default: physical_network_vswitch_mappings = *:external +# Example: physical_network_vswitch_mappings = net1:external1,net2:external2 +physical_network_vswitch_mappings = <%= node["openstack"]["network"]["hyperv"]["physical_network_vswitch_mappings"] %> + +# (StrOpt) Private virtual switch name used for local networking. +# +# Default: local_network_vswitch = private +# Example: local_network_vswitch = custom_vswitch +local_network_vswitch = <%= node["openstack"]["network"]["hyperv"]["local_network_vswitch"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb new file mode 100644 index 0000000..f5816f0 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/linuxbridge/linuxbridge_conf.ini.erb @@ -0,0 +1,67 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[VLANS] +# (StrOpt) Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST change this to +# 'vlan' and configure network_vlan_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to +# disable creation of tenant networks. +# +# Default: tenant_network_type = local +# Example: tenant_network_type = vlan +tenant_network_type = <%= node["openstack"]["network"]["linuxbridge"]["tenant_network_type"] %> + +# (ListOpt) Comma-separated list of +# [::] tuples enumerating ranges +# of VLAN IDs on named physical networks that are available for +# allocation. All physical networks listed are available for flat and +# VLAN provider network creation. Specified ranges of VLAN IDs are +# available for tenant network allocation if tenant_network_type is +# 'vlan'. If empty, only local networks may be created. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 +network_vlan_ranges = <%= node["openstack"]["network"]["linuxbridge"]["network_vlan_ranges"] %> + +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:nova@127.0.0.1:3306/quantum_linux_bridge +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[LINUX_BRIDGE] +# (ListOpt) Comma-separated list of +# : tuples mapping physical +# network names to the agent's node-specific physical network +# interfaces to be used for flat and VLAN networks. All physical +# networks listed in network_vlan_ranges on the server should have +# mappings to appropriate interfaces on each agent. +# +# Default: physical_interface_mappings = +# Example: physical_interface_mappings = physnet1:eth1 +physical_interface_mappings = <%= node["openstack"]["network"]["linuxbridge"]["physical_interface_mappings"] %> + +[AGENT] +# Agent's polling interval in seconds +polling_interval = 2 + +[SECURITYGROUP] +# Firewall driver for realizing quantum security group function +firewall_driver = quantum.agent.linux.iptables_firewall.IptablesFirewallDriver diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb new file mode 100644 index 0000000..9582a92 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/metaplugin/metaplugin.ini.erb @@ -0,0 +1,40 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> + +# Database reconnection retry times - in event connectivity is lost +# set to -1 implgies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 + +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[META] +## This is list of flavor:quantum_plugins +# extension method is used in the order of this list +plugin_list = <%= node["openstack"]["network"]["metaplugin"]["plugin_list"] %> +l3_plugin_list = <%= node["openstack"]["network"]["metaplugin"]["l3_plugin_list"] %> + +# Default value of flavor +default_flavor = <%= node["openstack"]["network"]["metaplugin"]["default_flavor"] %> +default_l3_flavor = <%= node["openstack"]["network"]["metaplugin"]["default_l3_flavor"] %> + +# supported extentions +supported_extension_aliases = providernet +# specific method map for each flavor to extensions +extension_map = get_port_stats:nvp diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb new file mode 100644 index 0000000..cc21d04 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/midonet/midonet.ini.erb @@ -0,0 +1,43 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:pass@127.0.0.1:3306/midonet_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[MIDONET] +# MidoNet API server URI +# midonet_uri = http://localhost:8080/midonet-api +midonet_uri = <%= node["openstack"]["network"]["midonet"]["midonet_uri"] %> + +# MidoNet admin username +username = <%= node["openstack"]["network"]["midonet"]["username"] %> + +# MidoNet admin password +password = <%= node["openstack"]["network"]["midonet"]["password"] %> + +# ID of the project that MidoNet admin user belongs to +project_id = <%= node["openstack"]["network"]["midonet"]["project_id"] %> + +# Virtual provider router ID +provider_router_id = <%= node["openstack"]["network"]["midonet"]["provider_router_id"] %> + +# Virtual metadata router ID +metadata_router_id = <%= node["openstack"]["network"]["midonet"]["metadata_router_id"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb new file mode 100644 index 0000000..5cebcac --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/nec/nec.ini.erb @@ -0,0 +1,56 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[OVS] +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch port". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity. +integration_bridge = <%= node["openstack"]["network"]["nec"]["integration_bridge"] %> + +[AGENT] +# Agent's polling interval in seconds +polling_interval = <%= node["openstack"]["network"]["nec"]["polling_interval"] %> + +# Use "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" to use the real +# root filter facility. +# Change to "sudo" to skip the filtering and just run the comand directly +root_helper = sudo + +[SECURITYGROUP] +# Firewall driver for realizing quantum security group function +firewall_driver = <%= node["openstack"]["network"]["nec"]["firewall_driver"] %> + +[OFC] +# Specify OpenFlow Controller Host, Port and Driver to connect. +host = <%= node["openstack"]["network"]["nec"]["ofc_host"] %> +port = <%= node["openstack"]["network"]["nec"]["ofc_port"] %> + +# Drivers are in quantum/plugins/nec/drivers/ . +driver = <%= node["openstack"]["network"]["nec"]["ofc_driver"] %> + +# PacketFilter is available when it's enabled in this configuration +# and supported by the driver. +enable_packet_filter = <%= node["openstack"]["network"]["nec"]["ofc_enable_packet_filter"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb new file mode 100644 index 0000000..b1e24a0 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/nicira/nvp.ini.erb @@ -0,0 +1,116 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +# ############################################################# +# WARNINGS: The following deprecations have been made in the +# Havana release. Support for the options below will be removed +# in Ixxx. +# +# Section: [DEFAULT], Option: 'metadata_dhcp_host_route' +# Remarks: Use 'enable_isolated_metadata' in dhcp_agent.ini. +# +# +# Section: [CLUSTER:name], Option: 'nvp_controller_connection' +# Remarks: The configuration will allow the specification of +# a single cluster, therefore [CLUSTER:name] is no +# longer used. Use 'nvp_*', options, 'req_timeout', +# 'retries', etc. as indicated in the DEFAULT section. +# Support for multiple clusters will be added through +# an API extension. +# ############################################################## + +[DEFAULT] +# User name for NVP controller +nvp_user = admin <%= node["openstack"]["network"]["nicira"]["nvp_user"] %> + +# Password for NVP controller +nvp_password = <%= node["openstack"]["network"]["nicira"]["nvp_password"] %> + +# Total time limit for a cluster request +# (including retries across different controllers) +req_timeout = <%= node["openstack"]["network"]["nicira"]["req_timeout"] %> + +# Time before aborting a request on an unresponsive controller +http_timeout = <%= node["openstack"]["network"]["nicira"]["http_timeout"] %> + +# Maximum number of times a particular request should be retried +retries = <%= node["openstack"]["network"]["nicira"]["retries"] %> + +# Maximum number of times a redirect response should be followed +redirects = <%= node["openstack"]["network"]["nicira"]["redirects"] %> + +# Comma-separated list of NVP controller endpoints (:). When port +# is omitted, 443 is assumed. This option MUST be specified, e.g.: +nvp_controllers = <%= node["openstack"]["network"]["nicira"]["nvp_controllers"] %> + +# UUID of the pre-existing default NVP Transport zone to be used for creating +# tunneled isolated "Quantum" networks. This option MUST be specified, e.g.: +default_tz_uuid = <%= node["openstack"]["network"]["nicira"]["default_tx_uuid"] %> + +# (Optional) UUID of the cluster in NVP. It can be retrieved from NVP management +# console "admin" section. +nvp_cluster_uuid = <%= node["openstack"]["network"]["nicira"]["nvp_cluster_uuid"] %> + +# (Optional) UUID for the default l3 gateway service to use with this cluster. +# To be specified if planning to use logical routers with external gateways. +default_l3_gw_service_uuid = <%= node["openstack"]["network"]["nicira"]["default_l3_gateway_service_uuid"] %> + +# (Optional) UUID for the default l2 gateway service to use with this cluster. +# To be specified for providing a predefined gateway tenant for connecting their networks. +default_l2_gw_service_uuid = <%= node["openstack"]["network"]["nicira"]["default_l2_gateway_service_uuid"] %> + +# Name of the default interface name to be used on network-gateway. This value +# will be used for any device associated with a network gateway for which an +# interface name was not specified +default_iface_name = <%= node["openstack"]["network"]["nicira"]["default_iface_name"] %> + + +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:quantum@127.0.0.1:3306/nvp_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> + +# Number of reconnection attempts to the DB; Set to -1 to try indefinitely +# sql_max_retries = 10 + +# Period between reconnection attempts to the DB +# reconnect_interval = 2 + +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. + +# sql_dbpool_enable = False + +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 + +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 + +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + + +[QUOTAS] +# number of network gateways allowed per tenant, -1 means unlimited +quota_network_gateway = <%= node["openstack"]["network"]["nicira"]["quota_network_gateway"] %> + + +[NVP] +# Maximum number of ports for each bridged logical switch +max_lp_per_bridged_ls = <%= node["openstack"]["network"]["nicira"]["max_lp_per_bridged_ls"] %> + +# Maximum number of ports for each overlay (stt, gre) logical switch +max_lp_per_overlay_ls = <%= node["openstack"]["network"]["nicira"]["max_lp_per_overlay_ls"] %> + +# Number of connects to each controller node. +concurrent_connections = <%= node["openstack"]["network"]["nicira"]["concurrent_connections"] %> + +# Acceptable values for 'metadata_mode' are: +# - 'access_network': this enables a dedicated connection to the metadata +# proxy for metadata server access via Quantum router. +# - 'dhcp_host_route': this enables host route injection via the dhcp agent. +# This option is only useful if running on a host that does not support +# namespaces otherwise access_network should be used. +metadata_mode = <%= node["openstack"]["network"]["nicira"]["metadata_mode"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb new file mode 100644 index 0000000..eef1862 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/openvswitch/ovs_quantum_plugin.ini.erb @@ -0,0 +1,155 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum +# Replace 127.0.0.1 above with the IP address of the database used by the +# main quantum server. (Leave it as is if the database runs on this host.) +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[OVS] +# (StrOpt) Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST either change this +# to 'vlan' and configure network_vlan_ranges below or change this to +# 'gre' and configure tunnel_id_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to +# disable creation of tenant networks. +# +# Default: tenant_network_type = local +# Example: tenant_network_type = gre +tenant_network_type = <%= node["openstack"]["network"]["openvswitch"]["tenant_network_type"] %> + +# (ListOpt) Comma-separated list of +# [::] tuples enumerating ranges +# of VLAN IDs on named physical networks that are available for +# allocation. All physical networks listed are available for flat and +# VLAN provider network creation. Specified ranges of VLAN IDs are +# available for tenant network allocation if tenant_network_type is +# 'vlan'. If empty, only gre and local networks may be created. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 +<% if node["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] != "nil" -%> +network_vlan_ranges = <%= node["openstack"]["network"]["openvswitch"]["network_vlan_ranges"] %> +<% end -%> + +# (BoolOpt) Set to True in the server and the agents to enable support +# for GRE networks. Requires kernel support for OVS patch ports and +# GRE tunneling. +# +# Default: enable_tunneling = False +enable_tunneling = <%= node["openstack"]["network"]["openvswitch"]["enable_tunneling"] %> + +# (ListOpt) Comma-separated list of : tuples +# enumerating ranges of GRE tunnel IDs that are available for tenant +# network allocation if tenant_network_type is 'gre'. +# +# Default: tunnel_id_ranges = +# Example: tunnel_id_ranges = 1:1000 +<% if node["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] -%> +tunnel_id_ranges = <%= node["openstack"]["network"]["openvswitch"]["tunnel_id_ranges"] %> +<% end -%> + +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch bay". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity. +# +# Default: integration_bridge = br-int +<% if node["openstack"]["network"]["openvswitch"]["integration_bridge"] -%> +integration_bridge = <%= node["openstack"]["network"]["openvswitch"]["integration_bridge"] %> +<% end -%> + +# Only used for the agent if tunnel_id_ranges (above) is not empty for +# the server. In most cases, the default value should be fine. +# +# Default: tunnel_bridge = br-tun +<% if node["openstack"]["network"]["openvswitch"]["tunnel_bridge"] -%> +tunnel_bridge = <%= node["openstack"]["network"]["openvswitch"]["tunnel_bridge"] %> +<% end -%> + +# Peer patch port in integration bridge for tunnel bridge +# Default: int_peer_patch_port = patch-tun +<% if node["openstack"]["network"]["openvswitch"]["int_peer_patch_port"] -%> +int_peer_patch_port = <%= node["openstack"]["network"]["openvswitch"]["int_peer_patch_port"] %> +<% end -%> + +# Peer patch port in tunnel bridge for integration bridge +# Default: tun_peer_patch_port = patch-int +<% if node["openstack"]["network"]["openvswitch"]["tun_peer_patch_port"] -%> +tun_peer_patch_port = <%= node["openstack"]["network"]["openvswitch"]["tun_peer_patch_port"] %> +<% end -%> + +# Uncomment this line for the agent if tunnel_id_ranges (above) is not +# empty for the server. Set local-ip to be the local IP address of +# this hypervisor. +# +# Default: local_ip = +local_ip = <%= @local_ip %> + +# (ListOpt) Comma-separated list of : tuples +# mapping physical network names to the agent's node-specific OVS +# bridge names to be used for flat and VLAN networks. The length of +# bridge names should be no more than 11. Each bridge must +# exist, and should have a physical network interface configured as a +# port. All physical networks listed in network_vlan_ranges on the +# server should have mappings to appropriate bridges on each agent. +# +# Default: bridge_mappings = +# Example: bridge_mappings = physnet1:br-eth1 +<% if node["openstack"]["network"]["openvswitch"]["bridge_mappings"] !="nil" -%> +bridge_mappings = <%= node["openstack"]["network"]["openvswitch"]["bridge_mappings"] %> +<% end -%> + +[AGENT] +# Agent's polling interval in seconds +polling_interval = 2 + +[SECURITYGROUP] +# Firewall driver for realizing quantum security group function +# Default: firewall_driver = quantum.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver +<% if node["openstack"]["network"]["openvswitch"]["fw_driver"] -%> +firewall_driver = <%= node["openstack"]["network"]["openvswitch"]["fw_driver"] %> +<% end -%> + +#----------------------------------------------------------------------------- +# Sample Configurations. +#----------------------------------------------------------------------------- +# +# 1. With VLANs on eth1. +# [DATABASE] +# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum +# [OVS] +# network_vlan_ranges = default:2000:3999 +# tunnel_id_ranges = +# integration_bridge = br-int +# bridge_mappings = default:br-eth1 +# [AGENT] +# Add the following setting, if you want to log to a file +# +# 2. With tunneling. +# [DATABASE] +# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum +# [OVS] +# network_vlan_ranges = +# tunnel_id_ranges = 1:1000 +# integration_bridge = br-int +# tunnel_bridge = br-tun +# local_ip = 10.0.0.3 diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb new file mode 100644 index 0000000..4d9945e --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/plumgrid/plumgrid.ini.erb @@ -0,0 +1,38 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: +# sql_connection = mysql://:@:3306/plumgrid_quantum +# Replace above with the IP address of the database used by the +# main quantum server. +sql_connection = <%= @sql_connection %> +# Database reconnection retry times - in event connectivity is lost +# set to -1 implies an infinite retry count +# sql_max_retries = 10 +# Database reconnection interval in seconds - if the initial connection to the +# database fails +# reconnect_interval = 2 +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[PLUMgridNOS] +# This line should be pointing to the NOS server, +# for the PLUMgrid platform. In other deployments, +# this is known as controller +nos_server = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> +nos_server_port = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> +# Authentification parameters for the NOS server. +# These are the admin credentials to manage and control +# the NOS server. +username = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> +password = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> +servertimeout = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> +# Name of the network topology to be deployed by NOS +topologyname = <%= node["openstack"]["network"]["plumgrid"]["nos_server"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb b/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb new file mode 100644 index 0000000..8915d79 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/plugins/ryu/ryu.ini.erb @@ -0,0 +1,58 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DATABASE] +# This line MUST be changed to actually run the plugin. +# Example: sql_connection = mysql://root:nova@127.0.0.1:3306/ryu_quantum +sql_connection = <%= @sql_connection %> +# Enable the use of eventlet's db_pool for MySQL. The flags sql_min_pool_size, +# sql_max_pool_size and sql_idle_timeout are relevant only if this is enabled. +# sql_dbpool_enable = False +# Minimum number of SQL connections to keep open in a pool +# sql_min_pool_size = 1 +# Maximum number of SQL connections to keep open in a pool +# sql_max_pool_size = 5 +# Timeout in seconds before idle sql connections are reaped +# sql_idle_timeout = 3600 + +[OVS] +# Do not change this parameter unless you have a good reason to. +# This is the name of the OVS integration bridge. There is one per hypervisor. +# The integration bridge acts as a virtual "patch port". All VM VIFs are +# attached to this bridge and then "patched" according to their network +# connectivity. +integration_bridge = <%= node["openstack"]["network"]["ryu"]["integration_bridge"] %> + +# openflow_rest_api = : +openflow_rest_api = <%= node["openstack"]["network"]["ryu"]["openflow_rest_api"] %> + +# tunnel key range: 0 < tunnel_key_min < tunnel_key_max +# VLAN: 12bits, GRE, VXLAN: 24bits +tunnel_key_min = <%= node["openstack"]["network"]["ryu"]["tunnel_key_min"] %> +tunnel_key_max = <%= node["openstack"]["network"]["ryu"]["tunnel_key_max"] %> + +# tunnel_ip = +# tunnel_interface = interface for tunneling +# when tunnel_ip is NOT specified, ip address is read +# from this interface +tunnel_ip = <%= node["openstack"]["network"]["ryu"]["tunnel_ip"] %> +tunnel_interface = <%= node["openstack"]["network"]["ryu"]["tunnel_interface"] %> + +# ovsdb_port = port number on which ovsdb is listening +# ryu-agent uses this parameter to setup ovsdb. +# ovs-vsctl set-manager ptcp: +# See set-manager section of man ovs-vsctl for details. +# currently ptcp is only supported. +# ovsdb_ip = +# ovsdb_interface = interface for ovsdb +# when ovsdb_addr NOT specifiied, ip address is gotten +# from this interface +ovsdb_port = <%= node["openstack"]["network"]["ryu"]["ovsdb_port"] %> +ovsdb_ip = <%= node["openstack"]["network"]["ryu"]["ovsdb_ip"] %> +ovsdb_interface = <%= node["openstack"]["network"]["ryu"]["ovsdb_interface"] %> + +[SECURITYGROUP] +# Firewall driver for realizing quantum security group function +firewall_driver = <%= node["openstack"]["network"]["ryu"]["firewall_driver"] %> + +[AGENT] +# Agent's polling interval in seconds +polling_interval = <%= node["openstack"]["network"]["ryu"]["polling_interval"] %> diff --git a/chef/cookbooks/openstack-network/templates/default/policy.json.erb b/chef/cookbooks/openstack-network/templates/default/policy.json.erb new file mode 100644 index 0000000..4262566 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/policy.json.erb @@ -0,0 +1,75 @@ +{ + "context_is_admin": "role:admin", + "admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s", + "admin_or_network_owner": "rule:context_is_admin or tenant_id:%(network_tenant_id)s", + "admin_only": "rule:context_is_admin", + "regular_user": "", + "shared": "field:networks:shared=True", + "external": "field:networks:router:external=True", + "default": "rule:admin_or_owner", + + "extension:provider_network:view": "rule:admin_only", + "extension:provider_network:set": "rule:admin_only", + + "extension:router:view": "rule:regular_user", + + "extension:port_binding:view": "rule:admin_only", + "extension:port_binding:set": "rule:admin_only", + + "subnets:private:read": "rule:admin_or_owner", + "subnets:private:write": "rule:admin_or_owner", + "subnets:shared:read": "rule:regular_user", + "subnets:shared:write": "rule:admin_only", + + "create_subnet": "rule:admin_or_network_owner", + "get_subnet": "rule:admin_or_owner or rule:shared", + "update_subnet": "rule:admin_or_network_owner", + "delete_subnet": "rule:admin_or_network_owner", + + "create_network": "", + "get_network": "rule:admin_or_owner or rule:shared or rule:external", + "create_network:shared": "rule:admin_only", + "create_network:router:external": "rule:admin_only", + "create_network:provider:network_type": "rule:admin_only", + "create_network:provider:physical_network": "rule:admin_only", + "create_network:provider:segmentation_id": "rule:admin_only", + "update_network": "rule:admin_or_owner", + "update_network:provider:network_type": "rule:admin_only", + "update_network:provider:physical_network": "rule:admin_only", + "update_network:provider:segmentation_id": "rule:admin_only", + "delete_network": "rule:admin_or_owner", + + "create_port": "", + "create_port:mac_address": "rule:admin_or_network_owner", + "create_port:fixed_ips": "rule:admin_or_network_owner", + "create_port:port_security_enabled": "rule:admin_or_network_owner", + "get_port": "rule:admin_or_owner", + "update_port": "rule:admin_or_owner", + "update_port:fixed_ips": "rule:admin_or_network_owner", + "update_port:port_security_enabled": "rule:admin_or_network_owner", + "delete_port": "rule:admin_or_owner", + + "extension:service_type:view_extended": "rule:admin_only", + "create_service_type": "rule:admin_only", + "update_service_type": "rule:admin_only", + "delete_service_type": "rule:admin_only", + "get_service_type": "rule:regular_user", + + "create_qos_queue": "rule:admin_only", + "get_qos_queue": "rule:admin_only", + "get_qos_queues": "rule:admin_only", + + "update_agent": "rule:admin_only", + "delete_agent": "rule:admin_only", + "get_agent": "rule:admin_only", + "get_agents": "rule:admin_only", + + "create_dhcp-network": "rule:admin_only", + "delete_dhcp-network": "rule:admin_only", + "get_dhcp-networks": "rule:admin_only", + "create_l3-router": "rule:admin_only", + "delete_l3-router": "rule:admin_only", + "get_l3-routers": "rule:admin_only", + "get_dhcp-agents": "rule:admin_only", + "get_l3-agents": "rule:admin_only" +} diff --git a/chef/cookbooks/openstack-network/templates/default/quantum-server.erb b/chef/cookbooks/openstack-network/templates/default/quantum-server.erb new file mode 100644 index 0000000..a23d8d6 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/quantum-server.erb @@ -0,0 +1,6 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +# defaults for quantum-server + +# path to config file corresponding to the core_plugin specified in +# quantum.conf +QUANTUM_PLUGIN_CONFIG=<%= @plugin_config %> diff --git a/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb b/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb new file mode 100644 index 0000000..4bcef2f --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/quantum-server.start.erb @@ -0,0 +1,95 @@ +#!/bin/sh +# +# quantum OpenStack Software Defined Networking Service +# +# chkconfig: - 98 02 +# description: quantum provides an API to \ +# * request and configure virtual networks +### END INIT INFO + +. /etc/rc.d/init.d/functions + +prog=quantum +exec="/usr/bin/$prog-server" +config="/etc/$prog/$prog.conf" +pidfile="/var/run/$prog/$prog.pid" +logfile="/var/log/$prog/server.log" + +[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog + +lockfile=/var/lock/subsys/$prog-server + +start() { + [ -x $exec ] || exit 5 + [ -f $config ] || exit 6 + echo -n $"Starting $prog: " + # the default ovs plugin path in the following daemon is not correct, cause the quantum server canot start, this template is going to fix it. + daemon --user quantum --pidfile $pidfile "$exec --config-file $config --config-file /etc/$prog/plugins/openvswitch/ovs_quantum_plugin.ini --log-file $logfile &>/dev/null & echo \$! > $pidfile" + retval=$? + echo + [ $retval -eq 0 ] && touch $lockfile + return $retval +} + +stop() { + echo -n $"Stopping $prog: " + killproc -p $pidfile $prog + retval=$? + echo + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +reload() { + restart +} + +force_reload() { + restart +} + +rh_status() { + status -p $pidfile $prog +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac +exit $? diff --git a/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb b/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb new file mode 100644 index 0000000..f2635ab --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/quantum.conf.erb @@ -0,0 +1,327 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> +[DEFAULT] +# Default log level is INFO +# verbose and debug has the same result. +# One of them will set DEBUG log level output +debug = <%= node["openstack"]["network"]["debug"] %> +verbose = <%= node["openstack"]["network"]["verbose"] %> + +# Where to store Quantum state files. This directory must be writable by the +# user executing the agent. +state_path = /var/lib/quantum + +# Where to store lock files +lock_path = $state_path/lock + +# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s +# log_date_format = %Y-%m-%d %H:%M:%S + +# use_syslog -> syslog +# log_file and log_dir -> log_dir/log_file +<% if node["openstack"]["network"]["syslog"]["use"] %> +log_config = /etc/openstack/logging.conf +<% end %> +# (not log_file) and log_dir -> log_dir/{binary_name}.log +# use_stderr -> stderr +# (not user_stderr) and (not log_file) -> stdout +# publish_errors -> notification system + +# use_syslog = False +# syslog_log_facility = LOG_USER + +# use_stderr = True +# log_file = +# log_dir = + +# publish_errors = False + +# Address to bind the API server +# bind_host = <%= @bind_address %> +bind_host = <%= node['openstack']['endpoints']['network-api']['host'] %> + +# Port the bind the API server to +bind_port = <%= @bind_port %> + +# Path to the extensions. Note that this can be a colon-separated list of +# paths. For example: +# api_extensions_path = extensions:/path/to/more/extensions:/even/more/extensions +# The __path__ of quantum.extensions is appended to this, so if your +# extensions are in there you don't need to specify them here +# api_extensions_path = + +# Quantum plugin provider module +# core_plugin = +core_plugin = <%= @core_plugin %> + +# Advanced service modules +# service_plugins = +<% if node["openstack"]["network"]["quantum_loadbalancer"] %> +# LoadBalancer plugin +service_plugins = quantum.plugins.services.agent_loadbalancer.plugin.LoadBalancerPlugin +<% end %> + +# Paste configuration file +api_paste_config = api-paste.ini + +# The strategy to be used for auth. +# Supported values are 'keystone'(default), 'noauth'. +# auth_strategy = keystone + +# Base MAC address. The first 3 octets will remain unchanged. If the +# 4h octet is not 00, it will also used. The others will be +# randomly generated. +# 3 octet +# base_mac = fa:16:3e:00:00:00 +# 4 octet +# base_mac = fa:16:3e:4f:00:00 + +# Maximum amount of retries to generate a unique MAC address +# mac_generation_retries = 16 + +# DHCP Lease duration (in seconds) +# dhcp_lease_duration = 120 + +# Allow sending resource operation notification to DHCP agent +# dhcp_agent_notification = True + +# Enable or disable bulk create/update/delete operations +# allow_bulk = True +# Enable or disable pagination +# allow_pagination = False +# Enable or disable sorting +# allow_sorting = False +# Enable or disable overlapping IPs for subnets +# Attention: the following parameter MUST be set to False if Quantum is +# being used in conjunction with nova security groups and/or metadata service. +# allow_overlapping_ips = False +# Ensure that configured gateway is on subnet +# force_gateway_on_subnet = False + + +# RPC configuration options. Defined in rpc __init__ +# The messaging module to use, defaults to kombu. +# rpc_backend = quantum.openstack.common.rpc.impl_kombu +# Size of RPC thread pool +# rpc_thread_pool_size = 64, +# Size of RPC connection pool +# rpc_conn_pool_size = 30 +# Seconds to wait for a response from call or multicall +# rpc_response_timeout = 60 +# Seconds to wait before a cast expires (TTL). Only supported by impl_zmq. +# rpc_cast_timeout = 30 +# Modules of exceptions that are permitted to be recreated +# upon receiving exception data from an rpc call. +# allowed_rpc_exception_modules = quantum.openstack.common.exception, nova.exception +# AMQP exchange to connect to if using RabbitMQ or QPID +control_exchange = quantum + +# Configuration options if sending notifications via kombu rpc (these are +# the defaults) +# SSL version to use (valid only if SSL enabled) +# kombu_ssl_version = +# SSL key file (valid only if SSL enabled) +# kombu_ssl_keyfile = +# SSL cert file (valid only if SSL enabled) +# kombu_ssl_certfile = +# SSL certification authority file (valid only if SSL enabled)' +# kombu_ssl_ca_certs = + +# allow_overlapping_ips = False +allow_overlapping_ips = <%= node["openstack"]["network"]["allow_overlapping_ips"] -%> + +##### RABBITMQ ##### +rabbit_userid=<%= node["openstack"]["network"]["rabbit"]["username"] %> +rabbit_password=<%= @rabbit_pass %> +rabbit_virtual_host=<%= node["openstack"]["network"]["rabbit"]["vhost"] %> +<% if node["openstack"]["network"]["rabbit"]["ha"] -%> +# Use HA queues in RabbitMQ (x-ha-policy: all).You need to +# wipe RabbitMQ database when changing this option. (boolean value) +rabbit_hosts=<%= @rabbit_hosts %> +rabbit_ha_queues=True +<% else -%> +rabbit_host=<%= node["openstack"]["network"]["rabbit"]["host"] %> +rabbit_port=<%= node["openstack"]["network"]["rabbit"]["port"] %> +<% end -%> +# Maximum retries with trying to connect to RabbitMQ +# (the default of 0 implies an infinite retry count) +# rabbit_max_retries = 0 +# RabbitMQ connection retry interval +# rabbit_retry_interval = 1 + +# QPID +# rpc_backend=quantum.openstack.common.rpc.impl_qpid +# Qpid broker hostname +# qpid_hostname = localhost +# Qpid broker port +# qpid_port = 5672 +# Qpid single or HA cluster (host:port pairs i.e: host1:5672, host2:5672) +# qpid_hosts is defaulted to '$qpid_hostname:$qpid_port' +# qpid_hosts = localhost:5672 +# Username for qpid connection +# qpid_username = '' +# Password for qpid connection +# qpid_password = '' +# Space separated list of SASL mechanisms to use for auth +# qpid_sasl_mechanisms = '' +# Seconds between connection keepalive heartbeats +# qpid_heartbeat = 60 +# Transport to use, either 'tcp' or 'ssl' +# qpid_protocol = tcp +# Disable Nagle algorithm +# qpid_tcp_nodelay = True + +# ZMQ +# rpc_backend=quantum.openstack.common.rpc.impl_zmq +# ZeroMQ bind address. Should be a wildcard (*), an ethernet interface, or IP. +# The "host" option should point or resolve to this address. +# rpc_zmq_bind_address = * + +# ============ Notification System Options ===================== + +# Notifications can be sent when network/subnet/port are create, updated or deleted. +# There are three methods of sending notifications: logging (via the +# log_file directive), rpc (via a message queue) and +# noop (no notifications sent, the default) + +# Notification_driver can be defined multiple times +# Do nothing driver +# notification_driver = quantum.openstack.common.notifier.no_op_notifier +# Logging driver +# notification_driver = quantum.openstack.common.notifier.log_notifier +# RPC driver. DHCP agents needs it. +notification_driver = quantum.openstack.common.notifier.rpc_notifier + +# default_notification_level is used to form actual topic name(s) or to set logging level +default_notification_level = INFO + +# default_publisher_id is a part of the notification payload +# host = myhost.com +# default_publisher_id = $host + +# Defined in rpc_notifier, can be comma separated values. +# The actual topic names will be %s.%(default_notification_level)s +notification_topics = notifications + +# Default maximum number of items returned in a single response, +# value == infinite and value < 0 means no max limit, and value must +# greater than 0. If the number of items requested is greater than +# pagination_max_limit, server will just return pagination_max_limit +# of number of items. +# pagination_max_limit = -1 + +# Maximum number of DNS nameservers per subnet +# max_dns_nameservers = 5 + +# Maximum number of host routes per subnet +# max_subnet_host_routes = 20 + +# Maximum number of fixed ips per port +# max_fixed_ips_per_port = 5 + +# =========== items for agent management extension ============= +# Seconds to regard the agent as down. +# agent_down_time = 5 +# =========== end of items for agent management extension ===== + +# =========== items for agent scheduler extension ============= +# Driver to use for scheduling network to DHCP agent +# network_scheduler_driver = <%= node["openstack"]["network"]["dhcp"]["scheduler"] %> +# Driver to use for scheduling router to a default L3 agent +# router_scheduler_driver = <%= node["openstack"]["network"]["l3"]["scheduler"] %> +# why? + + +# Allow auto scheduling networks to DHCP agent. It will schedule non-hosted +# networks to first DHCP agent which sends get_active_networks message to +# quantum server +# network_auto_schedule = True + +# Allow auto scheduling routers to L3 agent. It will schedule non-hosted +# routers to first L3 agent which sends sync_routers message to quantum server +# router_auto_schedule = True +# =========== end of items for agent scheduler extension ===== + +# =========== WSGI parameters related to the API server ============== +# Sets the value of TCP_KEEPIDLE in seconds to use for each server socket when +# starting API server. Not supported on OS X. +#tcp_keepidle = 600 + +# Number of seconds to keep retrying to listen +#retry_until_window = 30 + +# Number of backlog requests to configure the socket with. +#backlog = 4096 + +# Enable SSL on the API server +#use_ssl = False + +# Certificate file to use when starting API server securely +#ssl_cert_file = /path/to/certfile + +# Private key file to use when starting API server securely +#ssl_key_file = /path/to/keyfile + +# CA certificate file to use when starting API server securely to +# verify connecting clients. This is an optional parameter only required if +# API clients need to authenticate to the API server using SSL certificates +# signed by a trusted CA +#ssl_ca_file = /path/to/cafile +# ======== end of WSGI parameters related to the API server ========== + +[QUOTAS] +# resource name(s) that are supported in quota features +# quota_items = network,subnet,port + +# default number of resource allowed per tenant, minus for unlimited +# default_quota = -1 + +# number of networks allowed per tenant, and minus means unlimited +# quota_network = 10 + +# number of subnets allowed per tenant, and minus means unlimited +# quota_subnet = 10 + +# number of ports allowed per tenant, and minus means unlimited +# quota_port = 50 + +# number of security groups allowed per tenant, and minus means unlimited +# quota_security_group = 10 + +# number of security group rules allowed per tenant, and minus means unlimited +# quota_security_group_rule = 100 + +# default driver to use for quota checks +# quota_driver = quantum.quota.ConfDriver + +[DEFAULT_SERVICETYPE] +# Description of the default service type (optional) +# description = "default service type" +# Enter a service definition line for each advanced service provided +# by the default service type. +# Each service definition should be in the following format: +# :[:driver] + +[AGENT] +# Use "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" to use the real +# root filter facility. +# Change to "sudo" to skip the filtering and just run the comand directly +# root_helper = sudo +<% if node["openstack"]["network"]["use_rootwrap"] %> +root_helper = "sudo quantum-rootwrap /etc/quantum/rootwrap.conf" +<% end -%> + +# =========== items for agent management extension ============= +# seconds between nodes reporting state to server, should be less than +# agent_down_time +report_interval = <%= node["openstack"]["network"]["api"]["agent"]["agent_report_interval"] %> + +[keystone_authtoken] +auth_host = <%= @identity_endpoint.host %> +auth_port = <%= @identity_endpoint.port %> +auth_protocol = <%= @identity_endpoint.scheme %> +admin_tenant_name = <%= @service_tenant_name %> +admin_user = <%= @service_user %> +admin_password = <%= @service_pass %> +signing_dir = <%= node["openstack"]["network"]["api"]["agent"]["signing_dir"] %> + +# =========== end of items for agent management extension ===== diff --git a/chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb b/chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb new file mode 100644 index 0000000..f6408ce --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/quantum.sysconfig.erb @@ -0,0 +1,5 @@ +## Type: string +# +# location of the plugin configuration file + +QUANTUM_PLUGIN_CONF="/etc/quantum/plugins/<%= @plugin_conf %>" \ No newline at end of file diff --git a/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb b/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb new file mode 100644 index 0000000..234d281 --- /dev/null +++ b/chef/cookbooks/openstack-network/templates/default/rootwrap.conf.erb @@ -0,0 +1,6 @@ +<%= node["openstack"]["network"]["custom_template_banner"] %> + +[DEFAULT] +# List of directories to load filter definitions from (separated by ','). +# These directories MUST all be only writeable by root ! +filters_path=/etc/quantum/rootwrap.d,/usr/share/quantum/rootwrap diff --git a/chef/cookbooks/openstack-object-storage/Berksfile b/chef/cookbooks/openstack-object-storage/Berksfile new file mode 100644 index 0000000..ece8d4f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/Berksfile @@ -0,0 +1,4 @@ +metadata + +cookbook "statsd", + :git => "git://github.com/att-cloud/cookbook-statsd.git" diff --git a/chef/cookbooks/openstack-object-storage/Berksfile.lock b/chef/cookbooks/openstack-object-storage/Berksfile.lock new file mode 100644 index 0000000..f244cd2 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/Berksfile.lock @@ -0,0 +1,42 @@ +{ + "sources": { + "openstack-object-storage": { + "path": "." + }, + "statsd": { + "locked_version": "0.1.3", + "git": "git://github.com/att-cloud/cookbook-statsd.git", + "ref": "f759cd013c0a836f2acb219b3e006ff0a1308878" + }, + "memcached": { + "locked_version": "1.4.0" + }, + "runit": { + "locked_version": "1.1.6" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "yum": { + "locked_version": "2.3.0" + }, + "sysctl": { + "locked_version": "0.3.3" + }, + "apt": { + "locked_version": "2.1.0" + }, + "git": { + "locked_version": "2.5.2" + }, + "dmg": { + "locked_version": "1.1.0" + }, + "windows": { + "locked_version": "1.10.0" + }, + "chef_handler": { + "locked_version": "1.1.4" + } + } +} diff --git a/chef/cookbooks/openstack-object-storage/Gemfile b/chef/cookbooks/openstack-object-storage/Gemfile new file mode 100644 index 0000000..d592402 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.8" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "webmock", "~> 1.11.0" diff --git a/chef/cookbooks/openstack-object-storage/Gemfile.lock b/chef/cookbooks/openstack-object-storage/Gemfile.lock new file mode 100644 index 0000000..f69004d --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/Gemfile.lock @@ -0,0 +1,211 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + berkshelf (2.0.8) + activesupport (~> 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.9.0) + builder (>= 2.1.2) + crack (0.4.1) + safe_yaml (~> 0.9.0) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.8) + multipart-post (~> 1.2.0) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.2.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.1.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.5) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.24) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.2.0) + multi_json (1.7.9) + multi_xml (0.5.5) + multipart-post (1.2.0) + net-http-persistent (2.9) + net-ssh (2.6.8) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.5.0) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.18.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.5) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.5) + rspec-expectations (2.14.2) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.3) + rubyntlm (0.1.1) + safe_yaml (0.9.5) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.8.0) + strainer (3.2.2) + berkshelf (~> 2.0) + systemu (2.5.2) + thor (0.18.1) + timers (1.1.0) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + uuidtools (2.1.4) + varia_model (0.2.0) + buff-extensions (~> 0.2) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + webmock (1.11.0) + addressable (>= 2.2.7) + crack (>= 0.3.2) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.8) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + webmock (~> 1.11.0) diff --git a/chef/cookbooks/openstack-object-storage/README.md b/chef/cookbooks/openstack-object-storage/README.md new file mode 100644 index 0000000..5fb48a8 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/README.md @@ -0,0 +1,269 @@ +Description +=========== + +Installs the OpenStack Object Storage service **Swift** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/stackforge/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Swift is currently installed from packages. + +https://wiki.openstack.org/wiki/Swift + +Requirements +============ + +Clients +-------- + + * CentOS >= 6.3 + * Ubuntu >= 12.04 + +Chef +--------- + + * 11.4.4 + +Cookbooks +--------- + + * memcached + * sysctl + +Roles +===== + + * swift-account-server - storage node for account data + * swift-container-server - storage node for container data + * swift-object-server - storage node for object server + * swift-proxy-server - proxy for swift storge nodes + * swift-setup - server responsible for generating initial settings + * swift-management-server - responsible for ring generation + +The swift-management-server role performs the following functions: + + * proxy node that knows super admin password + * ring repository and ring building workstation + * generally always has the swift-setup role too + * there can only be _one_ swift-management-server + +There *must* be node with the the swift-managment-server role to act +as the ring repository. + +In small environments, it is likely that all storage machines will +have all-in-one roles, with a load balancer ahead of it + +In larger environments, where it is cost effective to split the proxy +and storage layer, storage nodes will carry +swift-{account,container,object}-server roles, and there will be +dedicated hosts with the swift-proxy-server role. + +In really really huge environments, it's possible that the storage +node will be split into swift-{container,accout}-server nodes and +swift-object-server nodes. + + +Attributes +========== + + * ```default[:swift][:authmode]``` - "swauth" or "keystone" (default "swauth"). Right now, only swauth is supported (defaults to swauth) + + * ```default[:swift][:swift_secret_databag_name]``` - this cookbook supports an optional secret databag where we will retrieve the following attributes overriding any default attributes below. (defaults to nil) + +``` + { + "id": "swift_dal2", + "swift_hash": "1a7c0568fa84" + "swift_authkey": "keY4all" + "dispersion_auth_user": "ops:dispersion", + "dispersion_auth_key": "dispersionpass" + } +``` + + * ```default[:swift][:swift_hash]``` - swift_hash_path_suffix in /etc/swift/swift.conf (defaults to 107c0568ea84) + + * ```default[:swift][:audit_hour]``` - Hour to run swift_auditor on storage nodes (defaults to 5) + + * ```default[:swift][:disk_enum_expr]``` - Eval-able expression that lists + candidate disk nodes for disk probing. The result shoule be a hash + with keys being the device name (without the leading "/dev/") and a + hash block of any extra info associated with the device. For + example: { "sdc" => { "model": "Hitachi 7K3000" }}. Largely, + though, if you are going to make a list of valid devices, you + probably know all the valid devices, and don't need to pass any + metadata about them, so { "sdc" => {}} is probably enough. Example + expression: Hash[('a'..'f').to_a.collect{|x| [ "sd{x}", {} ]}] + + * ```default[:swift][:ring][:part_power]``` - controls the size of the ring (defaults to 18) + + * ```default[:swift][:ring][:min_part_hours]``` - the minimum number of hours before swift is allowed to migrate a partition (defaults to 1) + + * ```default[:swift][:ring][:replicas]``` - how many replicas swift should retain (defaults to 3) + + * ```default[:swift][:disk_test_filter]``` - an array of expressions that must + all be true in order a block deviced to be considered for + formatting and inclusion in the cluster. Each rule gets evaluated + with "candidate" set to the device name (without the leading + "/dev/") and info set to the node hash value. Default rules: + + * "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ + /vd[^a]/" + + * "File.exists?('/dev/ + candidate)" + + * "not system('/sbin/sfdisk -V /dev/' + candidate + '>/dev/null 2>&2')" + + * "info['removable'] = 0" ]) + + * ```default[:swift][:expected_disks]``` - an array of device names that the + operator expecs to be identified by the previous two values. This + acts as a second-check on discovered disks. If this array doesn't + match the found disks, then chef processing will be stopped. + Example: ("b".."f").collect{|x| "sd#{x}"}. Default: none. + +There are other attributes that must be set depending on authmode. +For "swauth", the following attributes are used: + + * ```default[:swift][:authkey]``` - swauth super admin key if using swauth (defaults to test) + +In addition, because swift is typically deployed as a cluster +there are some attributes used to find interfaces and ip addresses +on storage nodes: + + * ```default[:swift][:git_builder_ip]``` - the IP address of the management server which other cluster members will use as their git pull target for ring updates (defaults to 127.0.0.1) + * ```default[:swift][:network][:proxy-bind-ip]``` - the IP address to bind to + on the proxy servers (defaults to 0.0.0.0 for all addresses) + * ```default[:swift][:network][:proxy-bind-port]``` - the port to bind to + on the proxy servers (defaults to 8080) + * ```default[:swift][:network][:account-bind-ip]``` - the IP address to bind to + on the account servers (defaults to 0.0.0.0 for all addresses) + * ```default[:swift][:network][:account-bind-port]``` - the port to bind to + on the account servers (defaults to 6002) + * ```default[:swift][:network][:container-bind-ip]``` - the IP address to bind to + on the container servers (defaults to 0.0.0.0 for all addresses) + * ```default[:swift][:network][:container-bind-port]``` - the port to bind to + on the container servers (defaults to 6002) + * ```default[:swift][:network][:object-bind-ip]``` - the IP address to bind to + on the object servers (defaults to 0.0.0.0 for all addresses) + * ```default[:swift][:network][:object-bind-port]``` - the port to bind to + on the container servers (defaults to 6002) + * ```default[:swift][:network][:object-cidr]``` - the CIDR network for your object + servers in order to build the ring (defaults to 10.0.0.0/24) + +Examples +======== + +Example environment +------------------- + +```json +{ + "default_attributes": { + "swift": { + "swift_hash": "107c0568ea84", + "authmode": "swauth", + "authkey": "test" + "auto_rebuild_rings": false + "git_builder_ip": "10.0.0.10" + "swauth": { + "url": "http://10.0.0.10:8080/v1/" + } + }, + }, + "name": "swift", + "chef_type": "environment", + "json_class": "Chef::Environment" +} +``` + +This sets up defaults for a swauth-based cluster with the storage +network on 10.0.0.0/24. + +Example all-in-one +-------------------------- + +Example all-in-one storage node config (note there should only ever be +one node with the swift-setup and swift-management roles) + +```json +{ + "id": "storage1", + "name": "storage1", + "json_class": "Chef::Node", + "run_list": [ + "role[swift-setup]", + "role[swift-management-server]", + "role[swift-account-server]", + "role[swift-object-server]", + "role[swift-container-server]", + "role[swift-proxy-server]" + ], + "chef_environment": "development", + "normal": { + "swift": { + "zone": "1" + } + } +} +``` + +Standalone Storage Server +------------------------- + +```json +{ + "name": "swift-object-server", + "json_class": "Chef::Role", + "run_list": [ + "recipe[swift::object-server]" + ], + "description": "A storage server role.", + "chef_type": "role" +} +``` + +Standalone Proxy Server +----------------------- + +```json + "run_list": [ + "role[swift-proxy-server]" + ] +``` + +Testing +======= + +This cookbook is using [ChefSpec](https://github.com/acrmp/chefspec) for testing. Run the following before commiting. It will run your tests, and check for lint errors. + + $ ./run_tests.bash + +There is also a Vagrant test environment that you can launch in order to integration +test this cookbook. See the tests/README.md file for more information on launching the environment. + +Testing +======= + + $ bundle install + $ bundle exec berks install + $ bundle exec strainer test + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Authors** | Alan Meadows () | +| | Oisin Feely () | +| | Ron Pedde () | +| | Will Kelly () | +| | | +| **Copyright** | Copyright (c) 2013, AT&T, Inc. | +| | Copyright (c) 2012, Rackspace US, Inc. | + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-object-storage/Strainerfile b/chef/cookbooks/openstack-object-storage/Strainerfile new file mode 100644 index 0000000..f23cd14 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/Strainerfile @@ -0,0 +1,4 @@ +# Strainerfile +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK diff --git a/chef/cookbooks/openstack-object-storage/attributes/default.rb b/chef/cookbooks/openstack-object-storage/attributes/default.rb new file mode 100644 index 0000000..a52f152 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/attributes/default.rb @@ -0,0 +1,194 @@ +#-------------------- +# node/ring settings +#-------------------- + +default["swift"]["state"] = {} +default["swift"]["swift_hash"] = "107c0568ea84" +default["swift"]["audit_hour"] = "5" +default["swift"]["disk_enum_expr"] = "node[:block_device]" +default["swift"]["auto_rebuild_rings"] = false +default["swift"]["git_builder_ip"] = "127.0.0.1" + +# the release only has any effect on ubuntu, and must be +# a valid release on http://ubuntu-cloud.archive.canonical.com/ubuntu +default["swift"]["release"] = "folsom" + +# we support an optional secret databag where we will retrieve the +# following attributes overriding any default attributes here +# +# { +# "id": "swift_dal2", +# "swift_hash": "107c0568ea84" +# "swift_authkey": "keW4all" +# "dispersion_auth_user": "test:test", +# "dispersion_auth_key": "test" +# } +default["swift"]["swift_secret_databag_name"] = nil + +#-------------------- +# authentication +#-------------------- + +default["swift"]["authmode"] = "swauth" +default["swift"]["authkey"] = "test" +default["swift"]["swift_url"] = "http://127.0.0.1:8080/v1/" +default["swift"]["swauth_url"] = "http://127.0.0.1:8080/v1/" +default["swift"]["auth_url"] = "http://127.0.0.1:8080/auth/v1.0" + +#--------------------- +# dispersion settings +#--------------------- + +default["swift"]["dispersion"]["auth_user"] = "test:test" +default["swift"]["dispersion"]["auth_key"] = "test" + + +# settings for the swift ring - these default settings are +# a safe setting for testing but part_power should be set to +# 26 in production to allow a swift cluster with 50,000 spindles +default["swift"]["ring"]["part_power"] = 18 +default["swift"]["ring"]["min_part_hours"] = 1 +default["swift"]["ring"]["replicas"] = 3 + +#------------------ +# statistics +#------------------ +default["swift"]["enable_statistics"] = true + +#------------------ +# network settings +#------------------ + +# the cidr configuration items are unimportant for a single server +# configuration, but in a multi-server setup, the cidr should match +# the interface appropriate to that service as they are used to +# resolve the appropriate addresses to use for internode +# communication + +# proxy servers +default["swift"]["network"]["proxy-bind-ip"] = "0.0.0.0" +default["swift"]["network"]["proxy-bind-port"] = "8080" +default["swift"]["network"]["proxy-cidr"] = "10.0.0.0/24" + +# account servers +default["swift"]["network"]["account-bind-ip"] = "0.0.0.0" +default["swift"]["network"]["account-bind-port"] = "6002" + +# container servers +default["swift"]["network"]["container-bind-ip"] = "0.0.0.0" +default["swift"]["network"]["container-bind-port"] = "6001" + +# object servers +default["swift"]["network"]["object-bind-ip"] = "0.0.0.0" +default["swift"]["network"]["object-bind-port"] = "6000" +default["swift"]["network"]["object-cidr"] = "10.0.0.0/24" + +#------------------ +# sysctl +#------------------ + +# set sysctl properties for time waits +default['sysctl']['params']['net']['ipv4']['tcp_tw_recycle'] = 1 +default['sysctl']['params']['net']['ipv4']['tcp_tw_reuse'] = 1 +default['sysctl']['params']['net']['ipv4']['tcp_syncookies'] = 0 + +# N.B. conntrack_max may also need to be adjusted if +# server is running a stateful firewall + +#------------------ +# disk search +#------------------ + +# disk_test_filter is an array of predicates to test against disks to +# determine if a disk should be formatted and configured for swift. +# Each predicate is evaluated in turn, and a false from the predicate +# will result in the disk not being considered as a candidate for +# formatting. +default["swift"]["disk_test_filter"] = [ "candidate =~ /(sd|hd|xvd|vd)(?!a$)[a-z]+/", + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s" ] + +#------------------ +# packages +#------------------ + + +# Leveling between distros +case platform +when "redhat" + default["swift"]["platform"] = { + "disk_format" => "ext4", + "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-memcached"], + "object_packages" => ["openstack-swift-object", "sudo", "cronie"], + "container_packages" => ["openstack-swift-container", "sudo", "cronie"], + "account_packages" => ["openstack-swift-account", "sudo", "cronie"], + "swift_packages" => ["openstack-swift", "sudo", "cronie"], + "swauth_packages" => ["openstack-swauth", "sudo", "cronie"], + "rsync_packages" => ["rsync"], + "git_packages" => ["xinetd", "git", "git-daemon"], + "service_prefix" => "openstack-", + "service_suffix" => "", + "git_dir" => "/var/lib/git", + "git_service" => "git", + "service_provider" => Chef::Provider::Service::Redhat, + "override_options" => "" + } +# +# python-iso8601 is a missing dependency for swift. +# https://bugzilla.redhat.com/show_bug.cgi?id=875948 +when "centos" + default["swift"]["platform"] = { + "disk_format" => "xfs", + "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-iso8601", "python-memcached" ], + "object_packages" => ["openstack-swift-object", "sudo", "cronie", "python-iso8601" ], + "container_packages" => ["openstack-swift-container", "sudo", "cronie", "python-iso8601" ], + "account_packages" => ["openstack-swift-account", "sudo", "cronie", "python-iso8601" ], + "swift_packages" => ["openstack-swift", "sudo", "cronie", "python-iso8601" ], + "swauth_packages" => ["openstack-swauth", "sudo", "cronie", "python-iso8601" ], + "rsync_packages" => ["rsync"], + "git_packages" => ["xinetd", "git", "git-daemon"], + "service_prefix" => "openstack-", + "service_suffix" => "", + "git_dir" => "/var/lib/git", + "git_service" => "git", + "service_provider" => Chef::Provider::Service::Redhat, + "override_options" => "" + } +when "fedora" + default["swift"]["platform"] = { + "disk_format" => "xfs", + "proxy_packages" => ["openstack-swift-proxy", "python-memcached"], + "object_packages" => ["openstack-swift-object"], + "container_packages" => ["openstack-swift-container"], + "account_packages" => ["openstack-swift-account"], + "swift_packages" => ["openstack-swift"], + "swauth_packages" => ["openstack-swauth"], + "rsync_packages" => ["rsync"], + "git_packages" => ["git", "git-daemon"], + "service_prefix" => "openstack-", + "service_suffix" => ".service", + "git_dir" => "/var/lib/git", + "git_service" => "git", + "service_provider" => Chef::Provider::Service::Systemd, + "override_options" => "" + } +when "ubuntu" + default["swift"]["platform"] = { + "disk_format" => "xfs", + "proxy_packages" => ["swift-proxy", "python-memcache"], + "object_packages" => ["swift-object"], + "container_packages" => ["swift-container"], + "account_packages" => ["swift-account", "python-swiftclient"], + "swift_packages" => ["swift"], + "swauth_packages" => ["swauth"], + "rsync_packages" => ["rsync"], + "git_packages" => ["git-daemon-sysvinit"], + "service_prefix" => "", + "service_suffix" => "", + "git_dir" => "/var/cache/git", + "git_service" => "git-daemon", + "service_provider" => Chef::Provider::Service::Upstart, + "override_options" => "-o Dpkg::Options:='--force-confold' -o Dpkg::Option:='--force-confdef'" + } +end diff --git a/chef/cookbooks/openstack-object-storage/files/default/5EDB1B62EC4926EA b/chef/cookbooks/openstack-object-storage/files/default/5EDB1B62EC4926EA new file mode 100644 index 0000000..99f7cac --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/5EDB1B62EC4926EA @@ -0,0 +1,53 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.11 (GNU/Linux) + +mQINBFAqSlgBEADPKwXUwqbgoDYgR20zFypxSZlSbrttOKVPEMb0HSUx9Wj8VvNC +r+mT4E9wAyq7NTIs5ad2cUhXoyenrjcfGqK6k9R6yRHDbvAxCSWTnJjw7mzsajDN +ocXC6THKVW8BSjrh0aOBLpht6d5QCO2vyWxw65FKM65GOsbX03ZngUPMuOuiOEHQ +Zo97VSH2pSB+L+B3d9B0nw3QnU8qZMne+nVWYLYRXhCIxSv1/h39SXzHRgJoRUFH +vL2aiiVrn88NjqfDW15HFhVJcGOFuACZnRA0/EqTq0qNo3GziQO4mxuZi3bTVL5s +GABiYW9uIlokPqcS7Fa0FRVIU9R+bBdHZompcYnKAeGag+uRvuTqC3MMRcLUS9Oi +/P9I8fPARXUPwzYN3fagCGB8ffYVqMunnFs0L6td08BgvWwer+Buu4fPGsQ5OzMc +lgZ0TJmXyOlIW49lc1UXnORp4sm7HS6okA7P6URbqyGbaplSsNUVTgVbi+vc8/jY +dfExt/3HxVqgrPlq9htqYgwhYvGIbBAxmeFQD8Ak/ShSiWb1FdQ+f7Lty+4mZLfN +8x4zPZ//7fD5d/PETPh9P0msF+lLFlP564+1j75wx+skFO4v1gGlBcDaeipkFzeo +zndAgpegydKSNTF4QK9iTYobTIwsYfGuS8rV21zE2saLM0CE3T90aHYB/wARAQAB +tD1DYW5vbmljYWwgQ2xvdWQgQXJjaGl2ZSBTaWduaW5nIEtleSA8ZnRwbWFzdGVy +QGNhbm9uaWNhbC5jb20+iQI3BBMBCAAhBQJQKkpYAhsDBQsJCAcDBRUKCQgLBRYC +AwEAAh4BAheAAAoJEF7bG2LsSSbqKxkQAIKtgImrk02YCDldg6tLt3b69ZK0kIVI +3Xso/zCBZbrYFmgGQEFHAa58mIgpv5GcgHHxWjpX3n4tu2RM9EneKvFjFBstTTgo +yuCgFr7iblvs/aMW4jFJAiIbmjjXWVc0CVB/JlLqzBJ/MlHdR9OWmojN9ZzoIA+i ++tWlypgUot8iIxkR6JENxit5v9dN8i6anmnWybQ6PXFMuNi6GzQ0JgZIVs37n0ks +2wh0N8hBjAKuUgqu4MPMwvNtz8FxEzyKwLNSMnjLAhzml/oje/Nj1GBB8roj5dmw +7PSul5pAqQ5KTaXzl6gJN5vMEZzO4tEoGtRpA0/GTSXIlcx/SGkUK5+lqdQIMdyS +n8bImU6V6rDSoOaI9YWHZtpv5WeUsNTdf68jZsFCRD+2+NEmIqBVm11yhmUoasC6 +dYw5l9P/PBdwmFm6NBUSEwxb+ROfpL1ICaZk9Jy++6akxhY//+cYEPLin02r43Z3 +o5Piqujrs1R2Hs7kX84gL5SlBzTM4Ed+ob7KVtQHTefpbO35bQllkPNqfBsC8AIC +8xvTP2S8FicYOPATEuiRWs7Kn31TWC2iwswRKEKVRmN0fdpu/UPdMikyoNu9szBZ +RxvkRAezh3WheJ6MW6Fmg9d+uTFJohZt5qHdpxYa4beuN4me8LF0TYzgfEbFT6b9 +D6IyTFoT0LequQINBFAqSlgBEADmL3TEq5ejBYrA+64zo8FYvCF4gziPa5rCIJGZ +/gZXQ7pm5zek/lOe9C80mhxNWeLmrWMkMOWKCeaDMFpMBOQhZZmRdakOnH/xxO5x ++fRdOOhy+5GTRJiwkuGOV6rB9eYJ3UN9caP2hfipCMpJjlg3j/GwktjhuqcBHXhA +HMhzxEOIDE5hmpDqZ051f8LGXld9aSL8RctoYFM8sgafPVmICTCq0Wh03dr5c2JA +gEXy3ushYm/8i2WFmyldo7vbtTfx3DpmJc/EMpGKV+GxcI3/ERqSkde0kWlmfPZb +o/5+hRqSryqfQtRKnFEQgAqAhPIwXwOkjCpPnDNfrkvzVEtl2/BWP/1/SOqzXjk9 +TIb1Q7MHANeFMrTCprzPLX6IdC4zLp+LpV91W2zygQJzPgWqH/Z/WFH4gXcBBqmI +8bFpMPONYc9/67AWUABo2VOCojgtQmjxuFn+uGNw9PvxJAF3yjl781PVLUw3n66d +wHRmYj4hqxNDLywhhnL/CC7KUDtBnUU/CKn/0Xgm9oz3thuxG6i3F3pQgpp7MeMn +tKhLFWRXo9Bie8z/c0NV4K5HcpbGa8QPqoDseB5WaO4yGIBOt+nizM4DLrI+v07y +Xe3Jm7zBSpYSrGarZGK68qamS3XPzMshPdoXXz33bkQrTPpivGYQVRZuzd/R6b+6 +IurV+QARAQABiQIfBBgBCAAJBQJQKkpYAhsMAAoJEF7bG2LsSSbq59EP/1U3815/ +yHV3cf/JeHgh6WS/Oy2kRHp/kJt3ev/l/qIxfMIpyM3u/D6siORPTUXHPm3AaZrb +w0EDWByA3jHQEzlLIbsDGZgrnl+mxFuHwC1yEuW3xrzgjtGZCJureZ/BD6xfRuRc +mvnetAZv/z98VN/oj3rvYhUi71NApqSvMExpNBGrdO6gQlI5azhOu8xGNy4OSke8 +J6pAsMUXIcEwjVEIvewJuqBW/3rj3Hh14tmWjQ7shNnYBuSJwbLeUW2e8bURnfXE +TxrCmXzDmQldD5GQWCcD5WDosk/HVHBmHlqrqy0VO2nE3c73dQlNcI4jVWeC4b4Q +SpYVsFz/6Iqy5ZQkCOpQ57MCf0B6P5nF92c5f3TYPMxHf0x3DrjDbUVZytxDiZZa +XsbZzsejbbc1bSNp4hb+IWhmWoFnq/hNHXzKPHBTapObnQju+9zUlQngV0BlPT62 +hOHOw3Pv7suOuzzfuOO7qpz0uAy8cFKe7kBtLSFVjBwaG5JX89mgttYW+lw9Rmsb +p9Iw4KKFHIBLOwk7s+u0LUhP3d8neBI6NfkOYKZZCm3CuvkiOeQP9/2okFjtj+29 +jEL+9KQwrGNFEVNe85Un5MJfYIjgyqX3nJcwypYxidntnhMhr2VD3HL2R/4CiswB +Oa4g9309p/+af/HU1smBrOfIeRoxb8jQoHu3 +=xg4S +-----END PGP PUBLIC KEY BLOCK----- + diff --git a/chef/cookbooks/openstack-object-storage/files/default/cluster_stats.py b/chef/cookbooks/openstack-object-storage/files/default/cluster_stats.py new file mode 100755 index 0000000..6f79362 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/cluster_stats.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +import os +import re +import errno +import subprocess + +USING_COLLECTD=0 + +try: + import collectd + USING_COLLECTD=1 +except: + pass + +# =============================================================================== +# [2012-06-19 21:37:04] Checking ring md5sum's on 3 hosts... +# 3/3 hosts matched, 0 error[s] while checking hosts. +# =============================================================================== +def get_md5sums(): + retval = 0 + output = subprocess.Popen(['swift-recon', '--objmd5'], stdout=subprocess.PIPE).communicate()[0] + for line in output.split("\n"): + result = re.search("([0-9]+) error", line) + if result: + retval = result.group(1) + return retval + + +# =============================================================================== +# [2012-06-19 21:36:27] Checking replication times on 3 hosts... +# [Replication Times] shortest: 0.00546943346659, longest: 0.00739345153173, avg: 0.00669538444943 +# =============================================================================== +def get_replication_times(): + retval = {} + output = subprocess.Popen(['swift-recon', '-r'], stdout=subprocess.PIPE).communicate()[0] + for line in output.split("\n"): + result = re.search("shortest: ([0-9\.]+), longest: ([0-9\.]+), avg: ([0-9\.]+)", line) + if result: + retval['shortest'] = float(result.group(1)) + retval['longest'] = float(result.group(2)) + retval['average'] = float(result.group(3)) + return retval + +def get_all(): + stats = {} + stats['md5sums'] = get_md5sums() + stats['replication_times'] = get_replication_times() + return stats + +def config_callback(conf): + pass + +def read_callback(): + stats = get_all() + + if not stats: + return + + # blarg, this should be fixed + for key in stats.keys(): + path = '%s' % key + value = stats[key] + + if type(value) != type({}): + # must be an int + val = collectd.Values(plugin=path) + val.type = 'gauge' + val.values = [int(value)] + val.dispatch() + else: + # must be a hash + for subvalue in value.keys(): + path = '%s.%s' % (key, subvalue) + val = collectd.Values(plugin=path) + val.type = 'gauge' + if type(value[subvalue]) == type("string"): + val.values = [int(value[subvalue])] + else: + val.values = value[subvalue] + val.dispatch() + +if not USING_COLLECTD: + stats = get_all() + print stats +else: + collectd.register_config(config_callback) + collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-object-storage/files/default/rsync.init b/chef/cookbooks/openstack-object-storage/files/default/rsync.init new file mode 100644 index 0000000..6eb27b0 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/rsync.init @@ -0,0 +1,44 @@ +#! /bin/sh +# +# chkconfig: 2345 50 50 +# description: rsync service + +# source function library + . /etc/rc.d/init.d/functions + +PROG='/usr/bin/rsync' +BASE=${0##*/} + +# Adapt the --config parameter to point to your rsync daemon configuration +# The config file must contain following line: +# pid file = /var/run/.pid +# Where is the filename of the init script (= this file) +OPTIONS="--daemon --config=/etc/rsyncd.conf" + +case "$1" in + start) + echo -n $"Starting $BASE: " + daemon --check $BASE $PROG $OPTIONS + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASE + echo + ;; + stop) + echo -n $"Shutting down $BASE: " + killproc $BASE + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASE + echo + ;; + restart|force-reload) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/chef/cookbooks/openstack-object-storage/files/default/rsync.service b/chef/cookbooks/openstack-object-storage/files/default/rsync.service new file mode 100644 index 0000000..172ba5e --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/rsync.service @@ -0,0 +1,11 @@ +[Unit] +Description=Rsync Server +After=local-fs.target + +[Service] +Type=forking +ExecStart=/usr/bin/rsync --daemon +PIDFile=/var/run/rsyncd.pid + +[Install] +WantedBy=multi-user.target diff --git a/chef/cookbooks/openstack-object-storage/files/default/swift_stats.py b/chef/cookbooks/openstack-object-storage/files/default/swift_stats.py new file mode 100755 index 0000000..dab33b9 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/files/default/swift_stats.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +import os +import errno + +from resource import getpagesize + +USING_COLLECTD=0 + +try: + import collectd + USING_COLLECTD=1 +except: + pass + +def get_unmounts(mountpath="/srv/node/"): + try: + candidates = [ x for x in os.listdir(mountpath) if os.path.isdir(mountpath + x) ] + except OSError as e: + if e.errno != errno.ENOENT: + raise + return 0 + + mounts = [] + with open('/proc/mounts', 'r') as procmounts: + for line in procmounts: + _, mounted_path, _, _, _, _ = line.rstrip().split() + if mounted_path.startswith(mountpath): + mounts.append(mounted_path.split('/')[-1]) + + return len(set(candidates) - set(mounts)) + +def get_sockstats(): + sockstat = {} + try: + with open('/proc/net/sockstat') as proc_sockstat: + for entry in proc_sockstat: + if entry.startswith("TCP: inuse"): + tcpstats = entry.split() + sockstat['tcp_in_use'] = int(tcpstats[2]) + sockstat['orphan'] = int(tcpstats[4]) + sockstat['time_wait'] = int(tcpstats[6]) + sockstat['tcp_mem_allocated_bytes'] = \ + int(tcpstats[10]) * getpagesize() + except OSError as e: + if e.errno != errno.ENOENT: + raise + try: + with open('/proc/net/sockstat6') as proc_sockstat6: + for entry in proc_sockstat6: + if entry.startswith("TCP6: inuse"): + sockstat['tcp6_in_use'] = int(entry.split()[2]) + except IOError as e: + if e.errno != errno.ENOENT: + raise + return sockstat + +def get_all(): + stats = {} + stats['socket'] = get_sockstats() + stats['unmounts'] = get_unmounts() + return stats + +def config_callback(conf): + pass + +def read_callback(): + stats = get_all() + + if not stats: + return + + # blarg, this should be fixed + for key in stats.keys(): + path = "%s" % key + value = stats[key] + + if type(value) != type({}): + # must be an int + val = collectd.Values(plugin=path) + val.type = 'gauge' + val.values = [int(value)] + val.dispatch() + else: + # must be a hash + for subvalue in value.keys(): + path = '%s.%s' % (key, subvalue) + val = collectd.Values(plugin=path) + val.type = 'gauge' + val.values = [int(value[subvalue])] + val.dispatch() + +if not USING_COLLECTD: + stats = get_all() + print stats +else: + collectd.register_config(config_callback) + collectd.register_read(read_callback) diff --git a/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb b/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb new file mode 100644 index 0000000..aef75d0 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/libraries/drive_utils.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: swift +# Library:: drive_utils +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# + +module DriveUtils + def locate_disks(enum_expression, filter_expressions) + candidate_disks = eval(enum_expression) + candidate_expression = "candidate_disks.select{|candidate,info| (" + + filter_expressions.map{|x| "(#{x})"}.join(" and ") + ")}" + # TODO(mancdaz): fix this properly so the above works in the first place + candidate_expression.gsub!(/\[\'removable\'\] = 0/, "['removable'].to_i == 0") + drives = Hash[eval(candidate_expression)] + Chef::Log.info("Using candidate drives: #{drives.keys.join(", ")}") + drives.keys + end +end + diff --git a/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb b/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb new file mode 100644 index 0000000..35c9710 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/libraries/ip_utils.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: swift +# Library:: ip_utils +# +# Copyright 2013, ATT Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Alan Meadows +# + +require "ipaddr" + +module IPUtils + def locate_ip_in_cidr(network, node) + Chef::Log.debug("Searching for ip within #{network} on node #{node.name}") + net = IPAddr.new(network) + node["network"]["interfaces"].each do |interface| + if interface[1].has_key?("addresses") then + interface[1]["addresses"].each do |k,v| + if v["family"] == "inet6" or (v["family"] == "inet" and v["prefixlen"] != "32") then + addr=IPAddr.new(k) + if net.include?(addr) then + return k + end + end + end + end + end + + error = "Can't find address within network #{network} for node #{node.name}" + Chef::Log.error(error) + raise error + end +end diff --git a/chef/cookbooks/openstack-object-storage/metadata.rb b/chef/cookbooks/openstack-object-storage/metadata.rb new file mode 100644 index 0000000..1022bc7 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/metadata.rb @@ -0,0 +1,19 @@ +name "openstack-object-storage" +maintainer "ATT, Inc." +license "Apache 2.0" +description "Installs and configures Openstack Swift" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.0" +recipe "openstack-object-storage::account-server", "Installs the swift account server" +recipe "openstack-object-storage::object-server", "Installs the swift object server" +recipe "openstack-object-storage::proxy-server", "Installs the swift proxy server" +recipe "openstack-object-storage::container-server", "Installs the swift container server" + +%w{ centos ubuntu }.each do |os| + supports os +end + +depends "memcached" +depends "sysctl" +depends "statsd" +depends "apt" diff --git a/chef/cookbooks/openstack-object-storage/providers/disk.rb b/chef/cookbooks/openstack-object-storage/providers/disk.rb new file mode 100644 index 0000000..b4c8433 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/providers/disk.rb @@ -0,0 +1,264 @@ +# +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: andi abes +# + +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut + +def load_current_resource + dev_name = @new_resource.name + @current = Chef::Resource::SwiftDisk.new(dev_name) + + parted_partition_parse dev_name + parts = @current.part() + + if not @current.blocks + # parted didn't return anything -- empty disk. + # get size from sfdisk + sfdisk_get_size(dev_name) + end + + Chef::Log.info("About to print partition table") + + s = < part_num, + :start => Regexp.last_match(2).to_i / 1024, + :end => Regexp.last_match(3).to_i / 1024, + :size => Regexp.last_match(4).to_i / 1024, + :type => Regexp.last_match(5), + :system => Regexp.last_match(6), + :flags => Regexp.last_match(7) } + part_tab << part_info + } + end + + @current.part(part_tab) + part_tab +end + +action :list do + Chef::Log.info("at some point there'll be a list") +end + +#### +# compare the requested partition table parameters to what exists +# if differences found - remove all current partitions, and create new ones. +# An existing partition is considered a match if: +# - it has the same serial # (1,2,3) +# - it has the same size +# +# We also want to start to partition at 1M to be correctly aligned +# even due to 4K sector size and controller stripe sizes. +# +# Plus, then parted doesn't bitch every time you run it. + +action :ensure_exists do + Chef::Log.info("Entering :ensure_exists") + + req = @new_resource.part + cur = @current.part + dev_name = @new_resource.name + update = false + + recreate, delete_existing = false + + disk_blocks = @current.blocks #1k blocks + + if (cur.nil?) + recreate = true; + else + idx = 0 + current_block=0 + + Chef::Log.info("Checking partition #{idx}") + + req.each { |params| + if (cur[idx].nil?) + recreate = true + Chef::Log.info("no current #{idx}") + next + end + + req_size = params[:size] # size in Mb - convert to blocks + if (req_size == :remaining) + req_size = disk_blocks - current_block + else + req_size = req_size * 1024 + end + + cur_size = cur[idx][:size] + + cur_min, cur_max = req_size*0.9, req_size*1.1 + if !(cur_size > cur_min and cur_size < cur_max) + recreate = true + end + + current_block += cur[idx][:size] + Chef::Log.info("partition #{idx} #{(recreate ? 'differs' : 'is same')}: #{cur_size}/#{req_size}") + idx+=1 + } + end + + if !recreate + Chef::Log.info("partition table matches - not recreating") + else + ### make sure to ensure that there are no mounted + ### filesystems on the device + re = /^(#{Regexp.escape(dev_name)}[0-9]+)/ + mounted = [] + shell_out!("mount").stdout.each_line { |line| + md = re.match(line) + next unless md + mounted << md[1] + } + mounted.each { |m| + Chef::Log.info("unmounting #{m}") + shell_out!("umount #{m}") + } + + # Nuke current partition table. + execute "create new partition table" do + command "parted -s -m #{dev_name} mktable gpt" + end + + # create new partitions + idx = 0 + req.each { | params | + start_block = 0 + + if idx == 0 + start_block = "1M" + end + + if (params[:size] == :remaining) + requested_size = "100%" + else + requested_size = "#{params[:size]}M" + end + + s = "parted -m -s #{dev_name} " + s << "mkpart #{idx} #{start_block} #{requested_size}" # #{params[:type]} + Chef::Log.info("creating new partition #{idx+1} with:" + s) + execute "creating partition #{idx}" do + command s + end + idx+=1 + + } + update = true + end + + # walk through the partitions and enforce disk format + idx=1 + req.each do |params| + device = "#{dev_name}#{idx}" + Chef::Log.info("Checking #{device}") + + if ::File.exist?(device) + # FIXME: check the format on the file system. This should be + # handled by a disk format provider. Maybe the xfs/btrfs/etc + # providers? + Chef::Log.info("Testing file system on #{device} for type #{params[:type]}") + + case params[:type] + when "xfs" + if not system("xfs_admin -l #{device}") + Mixlib::ShellOut.new("mkfs.xfs -f -i size=512 #{device}").run_command + update = true + end + when "ext4" + if not system("tune2fs -l #{device} | grep \"Filesystem volume name:\" | awk \'{print $4}\' | grep -v \"\"") + Mixlib::ShellOut.new("mkfs.ext4 #{device}").run_command + update = true + end + end + end + end + new_resource.updated_by_last_action(update) +end + diff --git a/chef/cookbooks/openstack-object-storage/providers/mounts.rb b/chef/cookbooks/openstack-object-storage/providers/mounts.rb new file mode 100644 index 0000000..bf2ed22 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/providers/mounts.rb @@ -0,0 +1,168 @@ +# +# Cookbook Name:: swift +# Provider:: mounts +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# + +require "chef/util/file_edit" + +action :ensure_exists do + proposed_devices = @new_resource.devices + path = @new_resource.name + dev_info = {} + + Chef::Log.info("IN MOUNTS") + + new_resource.updated_by_last_action(false) + + # walk through the devices, gathering information + proposed_devices.each do |device| + next if !::File.exists?("/dev/#{device}") + + info = {} + info["device"] = device + info["ip"] = @new_resource.ip + info["format"] = @new_resource.format + info["uuid"] = `blkid /dev/#{device} -s UUID -o value`.strip + info["mountpoint"] = info["uuid"].split("-").join("") + info["mounted"] = system("mount | grep '#{path}/#{info["mountpoint"]}\'") + info["size"] = `sfdisk -s /dev/#{device}`.to_i / 1024 + + next if (info["uuid"] == '') + + dev_info[info["uuid"]] = info + end + + Chef::Log.info("Physical Inventory:") + dev_info.each do |_,v| + Chef::Log.info("Device: #{v['device']}, UUID: #{v['uuid']}, Mounted: #{v['mounted']}, Format: #{v['format']}") + end + + # make sure we have a "path" + Directory(path) do + group "swift" + owner "swift" + recursive true + end.run_action(:create) + + # find what should be mounted, and what IS mounted + mounts=node["filesystem"].inject({}) { |hsh, (k,v)| hsh.merge(v["mount"] => k) } + valid_mounts = dev_info.inject([]) {|ary, (_,v)| ary << "#{path}/#{v['mountpoint']}"} + mountpoints = Dir.new(path).reject {|x| x[/^\./] }.collect { |d| "#{path}/#{d}" } + inverted_mounts = dev_info.inject({}) {|hsh,(k,v)| hsh.merge({v["mountpoint"] => v.merge("uuid" => k)})} + fstabs=::File.readlines("/etc/fstab").inject({}) do |hash,line| + line = line.split("#")[0].split() + Chef::Log.info("#{line[0]} ... #{line[1]}") + hash.merge(line[1] => line[0]) + end.reject { |k,v| !k or !v or !k.length or !v.length } + + Chef::Log.info("Mounts: #{mounts}") + Chef::Log.info("Valid Mounts: #{valid_mounts}") + Chef::Log.info("Mountpoints: #{mountpoints}") + Chef::Log.info("Fstabs: #{fstabs}") + + # mounts in /srv/node that shouldn't be there + (mounts.keys.select{|x| x and x[/^#{path}/]} - valid_mounts).each do |dev| + Chef::Log.info("Unmounting #{dev}") + system("umount #{dev}") if system("mount | grep '#{dev}'") + new_resource.updated_by_last_action(true) + end + + # fstab entries that don't need to be there anymore + (fstabs.keys.select {|k| k.start_with? path} - valid_mounts).each do |dev| + fe = Chef::Util::FileEdit.new("/etc/fstab") + fe.search_file_delete_line(Regexp.new(dev.gsub("/","\/"))) + fe.write_file + new_resource.updated_by_last_action(true) + end + + # directories/mountpoints in /srv/node that are now useless + (mountpoints - valid_mounts).each do |mountpoint| + Chef::Log.info("rmdiring #{mountpoint}") + begin + Dir.rmdir(mountpoint) + rescue SystemCallError + Chef::Log.info("Directory #{mountpoint} appears non-empty") + end + new_resource.updated_by_last_action(true) + end + + # new, unmounted devices + (valid_mounts - mounts.keys).each do |mountpoint| + info = inverted_mounts[mountpoint.gsub("#{path}/","")] + + Chef::Log.info("mounting #{mountpoint} (#{info['device']})") + + mount_path = "#{path}/#{info['mountpoint']}" + + Directory(mount_path) do + group "swift" + owner "swift" + recursive true + end.run_action(:create) + + case info['format'] + when 'ext4' + mount_options = "noatime,nodiratime,nobarrier,user_xattr" + when 'xfs' + case node["platform"] + when "ubuntu","debian" + mount_options = "noatime,nodiratime,nobarrier,logbufs=8,nobootwait" + else + mount_options = "noatime,nodiratime,nobarrier,logbufs=8" + end + end + + mt = Mount(mount_path) do + device info['uuid'] + device_type :uuid + options mount_options + dump 0 + fstype info['format'] + action :nothing + end + + if not fstabs.has_key?(mount_path) + # then its a brand-new drive, freshly formatted + Chef::Log.info("Mounting new device #{info['mountpoint']}") + mt.run_action(:enable) + mt.run_action(:mount) + end + + new_resource.updated_by_last_action(true) + end + + dev_info.reject { |k,v| v["mounted"] }.keys.each do |uuid| + dev_info[uuid]["mounted"] = system("mount | grep '#{path}/#{dev_info[uuid]["mountpoint"]}\'") + end + + if @new_resource.publish_attributes and dev_info != {} + dev_info.each do |k,v| + node.set["swift"]["state"]["devs"][k] = { + :device => v["device"], + :size => v["size"], + :uuid => v["uuid"], + :mounted => v["mounted"], + :format => v["format"], + :mountpoint => v["mountpoint"], + :ip => v["ip"] + } + end + end +end + diff --git a/chef/cookbooks/openstack-object-storage/providers/ring_script.rb b/chef/cookbooks/openstack-object-storage/providers/ring_script.rb new file mode 100644 index 0000000..7218a39 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/providers/ring_script.rb @@ -0,0 +1,262 @@ +# +# Cookbook Name:: swift +# Resource:: ring_script +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# + +require "pp" + +def generate_script + # need to load and parse the existing rings. + ports = { "object" => "6000", "container" => "6001", "account" => "6002" } + must_rebalance = false + + ring_path = @new_resource.ring_path + ring_data = { :raw => {}, :parsed => {}, :in_use => {} } + disk_data = {} + dirty_cluster_reasons = [] + + [ "account", "container", "object" ].each do |which| + ring_data[:raw][which] = nil + + if ::File.exist?("#{ring_path}/#{which}.builder") + IO.popen("su swift -c 'swift-ring-builder #{ring_path}/#{which}.builder'") do |pipe| + ring_data[:raw][which] = pipe.readlines + # Chef::Log.debug("#{ which.capitalize } Ring data: #{ring_data[:raw][which]}") + ring_data[:parsed][which] = parse_ring_output(ring_data[:raw][which]) + + node.set["swift"]["state"]["ring"][which] = ring_data[:parsed][which] + end + else + Chef::Log.info("#{which.capitalize} ring builder files do not exist!") + end + + # collect all the ring data, and note what disks are in use. All I really + # need is a hash of device and id + + ring_data[:in_use][which] ||= {} + if ring_data[:parsed][which][:hosts] + ring_data[:parsed][which][:hosts].each do |ip, dev| + dev.each do |dev_id, devhash| + ring_data[:in_use][which].store(devhash[:device], devhash[:id]) + end + end + end + + Chef::Log.debug("#{which.capitalize} Ring - In use: #{PP.pp(ring_data[:in_use][which],dump='')}") + + # figure out what's present in the cluster + disk_data[which] = {} + disk_state,_,_ = Chef::Search::Query.new.search(:node,"chef_environment:#{node.chef_environment} AND roles:swift-#{which}-server") + + # for a running track of available disks + disk_data[:available] ||= {} + disk_data[:available][which] ||= {} + + disk_state.each do |swiftnode| + if swiftnode[:swift][:state] and swiftnode[:swift][:state][:devs] + swiftnode[:swift][:state][:devs].each do |k,v| + disk_data[which][v[:ip]] = disk_data[which][v[:ip]] || {} + disk_data[which][v[:ip]][k] = {} + v.keys.each { |x| disk_data[which][v[:ip]][k].store(x,v[x]) } + + if swiftnode[:swift].has_key?("#{which}-zone") + disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["#{which}-zone"] + elsif swiftnode[:swift].has_key?("zone") + disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["zone"] + else + raise "Node #{swiftnode[:hostname]} has no zone assigned" + end + + disk_data[:available][which][v[:mountpoint]] = v[:ip] + + if not v[:mounted] + dirty_cluster_reasons << "Disk #{v[:name]} (#{v[:uuid]}) is not mounted on host #{v[:ip]} (#{swiftnode[:hostname]})" + end + end + end + end + Chef::Log.debug("#{which.capitalize} Ring - Avail: #{PP.pp(disk_data[:available][which],dump='')}") + end + + # Have the raw data, now bump it together and drop the script + + s = "#!/bin/bash\n\n# This script is automatically generated.\n" + s << "# Running it will likely blow up your system if you don't review it carefully.\n" + s << "# You have been warned.\n\n" + if not node["swift"]["auto_rebuild_rings"] + s << "if [ \"$1\" != \"--force\" ]; then\n" + s << " echo \"Auto rebuild rings is disabled, so you must use --force to generate rings\"\n" + s << " exit 0\n" + s << "fi\n\n" + end + + # Chef::Log.debug("#{PP.pp(disk_data, dump='')}") + + new_disks = {} + missing_disks = {} + new_servers = [] + + [ "account", "container", "object" ].each do |which| + # remove available disks that are already in the ring + new_disks[which] = disk_data[:available][which].reject{ |k,v| ring_data[:in_use][which].has_key?(k) } + + # find all in-ring disks that are not in the cluster + missing_disks[which] = ring_data[:in_use][which].reject{ |k,v| disk_data[:available][which].has_key?(k) } + + Chef::Log.debug("#{which.capitalize} Ring - Missing: #{PP.pp(missing_disks[which],dump='')}") + Chef::Log.debug("#{which.capitalize} Ring - New: #{PP.pp(new_disks[which],dump='')}") + + s << "\n# -- #{which.capitalize} Servers --\n\n" + disk_data[which].keys.sort.each do |ip| + s << "# #{ip}\n" + disk_data[which][ip].keys.sort.each do |k| + v = disk_data[which][ip][k] + s << "# " + v.keys.sort.select{|x| ["ip", "device", "uuid"].include?(x)}.collect{|x| v[x] }.join(", ") + if new_disks[which].has_key?(v["mountpoint"]) + s << " (NEW!)" + new_servers << ip unless new_servers.include?(ip) + end + s << "\n" + end + end + + # for all those servers, check if they are already in the ring. If not, + # then we need to add them to the ring. For those that *were* in the + # ring, and are no longer in the ring, we need to delete those. + + s << "\n" + + # add the new disks + disk_data[which].keys.sort.each do |ip| + disk_data[which][ip].keys.sort.each do |uuid| + v = disk_data[which][ip][uuid] + if new_disks[which].has_key?(v['mountpoint']) + s << "swift-ring-builder #{ring_path}/#{which}.builder add z#{v['zone']}-#{v['ip']}:#{ports[which]}/#{v['mountpoint']} #{v['size']}\n" + must_rebalance = true + end + end + end + + # remove the disks -- sort to ensure consistent order + missing_disks[which].keys.sort.each do |mountpoint| + diskinfo=ring_data[:parsed][which][:hosts].select{|k,v| v.has_key?(mountpoint)}.collect{|_,v| v[mountpoint]}[0] + Chef::Log.debug("Missing diskinfo: #{PP.pp(diskinfo,dump='')}") + description = Hash[diskinfo.select{|k,v| [:zone, :ip, :device].include?(k)}].collect{|k,v| "#{k}: #{v}" }.join(", ") + s << "# #{description}\n" + s << "swift-ring-builder #{ring_path}/#{which}.builder remove d#{missing_disks[which][mountpoint]}\n" + must_rebalance = true + end + + s << "\n" + + if(must_rebalance) + s << "swift-ring-builder #{ring_path}/#{which}.builder rebalance\n\n\n" + else + s << "# #{which.capitalize} ring has no outstanding changes!\n\n" + end + + # we'll only rebalance if we meet the minimums for new adds + if node["swift"].has_key?("wait_for") + if node["swift"]["wait_for"] > new_servers.count + Chef::Log.debug("New servers, but not enough to force a rebalance") + must_rebalance = false + end + end + end + [ s, must_rebalance ] +end + +# Parse the raw output of swift-ring-builder +def parse_ring_output(ring_data) + output = { :state => {} } + + ring_data.each do |line| + if line =~ /build version ([0-9]+)/ + output[:state][:build_version] = $1 + elsif line =~ /^Devices:\s+id\s+region\s+zone\s+/ + next + elsif line =~ /^Devices:\s+id\s+zone\s+/ + next + elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/ + output[:hosts] ||= {} + output[:hosts][$3] ||= {} + + output[:hosts][$3][$5] = {} + + output[:hosts][$3][$5][:id] = $1 + output[:hosts][$3][$5][:region] = $2 + output[:hosts][$3][$5][:zone] = $3 + output[:hosts][$3][$5][:ip] = $4 + output[:hosts][$3][$5][:port] = $5 + output[:hosts][$3][$5][:device] = $6 + output[:hosts][$3][$5][:weight] = $7 + output[:hosts][$3][$5][:partitions] = $8 + output[:hosts][$3][$5][:balance] = $9 + elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/ + output[:hosts] ||= {} + output[:hosts][$3] ||= {} + + output[:hosts][$3][$5] = {} + + output[:hosts][$3][$5][:id] = $1 + output[:hosts][$3][$5][:zone] = $2 + output[:hosts][$3][$5][:ip] = $3 + output[:hosts][$3][$5][:port] = $4 + output[:hosts][$3][$5][:device] = $5 + output[:hosts][$3][$5][:weight] = $6 + output[:hosts][$3][$5][:partitions] = $7 + output[:hosts][$3][$5][:balance] = $8 + elsif line =~ /(\d+) partitions, (\d+\.\d+) replicas, (\d+) regions, (\d+) zones, (\d+) devices, (\d+\.\d+) balance$/ + output[:state][:partitions] = $1 + output[:state][:replicas] = $2 + output[:state][:regions] = $3 + output[:state][:zones] = $4 + output[:state][:devices] = $5 + output[:state][:balance] = $6 + elsif line =~ /(\d+) partitions, (\d+) replicas, (\d+) zones, (\d+) devices, (\d+\.\d+) balance$/ + output[:state][:partitions] = $1 + output[:state][:replicas] = $2 + output[:state][:zones] = $3 + output[:state][:devices] = $4 + output[:state][:balance] = $5 + elsif line =~ /^The minimum number of hours before a partition can be reassigned is (\d+)$/ + output[:state][:min_part_hours] = $1 + else + raise "Cannot parse ring builder output for #{line}" + end + end + + output +end + +action :ensure_exists do + Chef::Log.debug("Ensuring #{new_resource.name}") + new_resource.updated_by_last_action(false) + s,must_update = generate_script + + script_file = File new_resource.name do + owner new_resource.owner + group new_resource.group + mode new_resource.mode + content s + end + + script_file.run_action(:create) + new_resource.updated_by_last_action(must_update) +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/account-server.rb b/chef/cookbooks/openstack-object-storage/recipes/account-server.rb new file mode 100644 index 0000000..ad7de4e --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/account-server.rb @@ -0,0 +1,96 @@ +# +# Cookbook Name:: swift +# Recipe:: account-server +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-object-storage::common" +include_recipe "openstack-object-storage::storage-common" +include_recipe "openstack-object-storage::disks" + +platform_options = node["swift"]["platform"] + +platform_options["account_packages"].each.each do |pkg| + package pkg do + action :install + options platform_options["override_options"] # retain configs + end +end + +# epel/f-17 missing init scripts for the non-major services. +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor reaper replicator}.each do |svc| + template "/etc/systemd/system/openstack-swift-account-#{svc}.service" do + owner "root" + group "root" + mode "0644" + source "simple-systemd-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Account #{svc.capitalize}", + :user => "swift", + :exec => "/usr/bin/swift-account-#{svc} " + + "/etc/swift/account-server.conf" + }) + only_if { platform?(%w{fedora}) } + end +end + +# TODO(breu): track against upstream epel packages to determine if this +# is still necessary +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor reaper replicator}.each do |svc| + template "/etc/init.d/openstack-swift-account-#{svc}" do + owner "root" + group "root" + mode "0755" + source "simple-redhat-init-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Account #{svc.capitalize}", + :exec => "account-#{svc}" + }) + only_if { platform?(%w{redhat centos}) } + end +end + +%w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc| + service_name = platform_options["service_prefix"] + svc + platform_options["service_suffix"] + service svc do + service_name service_name + provider platform_options["service_provider"] + supports :status => true, :restart => true + action [:enable, :start] + only_if "[ -e /etc/swift/account-server.conf ] && [ -e /etc/swift/account.ring.gz ]" + end +end + +# retrieve bind information from node +bind_ip = node["swift"]["network"]["bind_ip"] +bind_port = node["swift"]["network"]["bind_port"] + +# create account server template +template "/etc/swift/account-server.conf" do + source "account-server.conf.erb" + owner "swift" + group "swift" + mode "0600" + variables("bind_ip" => node["swift"]["network"]["account-bind-ip"], + "bind_port" => node["swift"]["network"]["account-bind-port"]) + + notifies :restart, "service[swift-account]", :immediately + notifies :restart, "service[swift-account-auditor]", :immediately + notifies :restart, "service[swift-account-reaper]", :immediately + notifies :restart, "service[swift-account-replicator]", :immediately +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/common.rb b/chef/cookbooks/openstack-object-storage/recipes/common.rb new file mode 100644 index 0000000..74dcff0 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/common.rb @@ -0,0 +1,110 @@ +# +# Cookbook Name:: swift +# Recipe:: swift-common +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef::Recipe + include DriveUtils +end + +include_recipe 'sysctl::default' + +# optionally statsd daemon for stats collection +if node["swift"]["enable_statistics"] + include_recipe 'statsd::server' +end + +platform_options = node["swift"]["platform"] + +# update repository if requested with the ubuntu cloud +case node["platform"] +when "ubuntu" + + Chef::Log.info("Creating apt repository for http://ubuntu-cloud.archive.canonical.com/ubuntu") + Chef::Log.info("chefspec: #{node['lsb']['codename']}-updates/#{node['swift']['release']}") + apt_repository "ubuntu_cloud" do + uri "http://ubuntu-cloud.archive.canonical.com/ubuntu" + distribution "#{node['lsb']['codename']}-updates/#{node['swift']['release']}" + components ["main"] + key "5EDB1B62EC4926EA" + action :add + end +end + + +platform_options["swift_packages"].each do |pkg| + package pkg do + action :install + end +end + +directory "/etc/swift" do + action :create + owner "swift" + group "swift" + mode "0700" + only_if "/usr/bin/id swift" +end + +# determine hash +if node['swift']['swift_secret_databag_name'].nil? + swifthash = node['swift']['swift_hash'] +else + swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + swifthash = swift_secrets['swift_hash'] +end + + +file "/etc/swift/swift.conf" do + action :create + owner "swift" + group "swift" + mode "0700" + content "[swift-hash]\nswift_hash_path_suffix=#{swifthash}\n" + only_if "/usr/bin/id swift" +end + +# need a swift user +user "swift" do + shell "/bin/bash" + action :modify + only_if "/usr/bin/id swift" +end + +package "git" do + action :install +end + +# drop a ring puller script +# TODO: make this smarter +git_builder_ip = node["swift"]["git_builder_ip"] +template "/etc/swift/pull-rings.sh" do + source "pull-rings.sh.erb" + owner "swift" + group "swift" + mode "0700" + variables({ + :builder_ip => git_builder_ip, + :service_prefix => platform_options["service_prefix"] + }) + only_if "/usr/bin/id swift" +end + +execute "/etc/swift/pull-rings.sh" do + cwd "/etc/swift" + only_if "[ -x /etc/swift/pull-rings.sh ]" +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/container-server.rb b/chef/cookbooks/openstack-object-storage/recipes/container-server.rb new file mode 100644 index 0000000..e0c0aee --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/container-server.rb @@ -0,0 +1,93 @@ +# +# Cookbook Name:: swift +# Recipe:: swift-container-server +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-object-storage::common" +include_recipe "openstack-object-storage::storage-common" +include_recipe "openstack-object-storage::disks" + + +platform_options = node["swift"]["platform"] + +platform_options["container_packages"].each do |pkg| + package pkg do + action :install + options platform_options["override_options"] + end +end + +# epel/f-17 missing init scripts for the non-major services. +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor updater replicator}.each do |svc| + template "/etc/systemd/system/openstack-swift-container-#{svc}.service" do + owner "root" + group "root" + mode "0644" + source "simple-systemd-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Container #{svc.capitalize}", + :user => "swift", + :exec => "/usr/bin/swift-container-#{svc} " + + "/etc/swift/container-server.conf" + }) + only_if { platform?(%w{fedora}) } + end +end + +# TODO(breu): track against upstream epel packages to determine if this +# is still necessary +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor updater replicator}.each do |svc| + template "/etc/init.d/openstack-swift-container-#{svc}" do + owner "root" + group "root" + mode "0755" + source "simple-redhat-init-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Container #{svc.capitalize}", + :exec => "container-#{svc}" + }) + only_if { platform?(%w{redhat centos}) } + end +end + +%w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc| + service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"] + + service svc do + service_name service_name + provider platform_options["service_provider"] + supports :status => true, :restart => true + action [:enable, :start] + only_if "[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]" + end +end + +template "/etc/swift/container-server.conf" do + source "container-server.conf.erb" + owner "swift" + group "swift" + mode "0600" + variables("bind_ip" => node["swift"]["network"]["container-bind-ip"], + "bind_port" => node["swift"]["network"]["container-bind-port"]) + + notifies :restart, "service[swift-container]", :immediately + notifies :restart, "service[swift-container-replicator]", :immediately + notifies :restart, "service[swift-container-updater]", :immediately + notifies :restart, "service[swift-container-auditor]", :immediately +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/disks.rb b/chef/cookbooks/openstack-object-storage/recipes/disks.rb new file mode 100644 index 0000000..89c573f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/disks.rb @@ -0,0 +1,66 @@ +# +# Cookbook Name:: swift +# Recipe:: disks +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# Inspired by: Andi Abes @ Dell + +class Chef::Recipe + include IPUtils +end + + +platform_options = node["swift"]["platform"] + +package "xfsprogs" do + action :install + only_if { platform?(%w{ubuntu debian fedora centos}) } +end + +%w(parted util-linux).each do |pkg| + package pkg do + action :install + end +end + +disk_enum_expr = node["swift"]["disk_enum_expr"] +disk_test_filter = node["swift"]["disk_test_filter"] + +disks = locate_disks(disk_enum_expr, disk_test_filter) + +disks.each do |disk| + openstack_object_storage_disk "/dev/#{disk}" do + part [{:type => platform_options["disk_format"] , :size => :remaining}] + action :ensure_exists + end +end + +# FIXME: "#{x}1" is only really valid for {v,s,h}dx. Doesn't +# work for loop or probably for hp-style /dev/cciss/c0d0p1x0t0g0m1whatever +# +# additionally, there is an implicit assumption that bind ports +# for all object/container/account services are on the same net +disk_ip = locate_ip_in_cidr(node["swift"]["network"]["object-cidr"], node) + +openstack_object_storage_mounts "/srv/node" do + action :ensure_exists + publish_attributes "swift/state/devs" + devices disks.collect { |x| "#{x}1" } + ip disk_ip + format platform_options["disk_format"] +end + diff --git a/chef/cookbooks/openstack-object-storage/recipes/management-server.rb b/chef/cookbooks/openstack-object-storage/recipes/management-server.rb new file mode 100644 index 0000000..db29e1d --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/management-server.rb @@ -0,0 +1,55 @@ +# +# Cookbook Name:: swift +# Recipe:: management-server +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-object-storage::common" + +# FIXME: This should probably be a role (ring-builder?), so you don't end up +# with multiple repos! +include_recipe "openstack-object-storage::ring-repo" + +platform_options = node["swift"]["platform"] + +if node["swift"]["authmode"] == "swauth" + platform_options["swauth_packages"].each.each do |pkg| + package pkg do + action :install + options platform_options["override_options"] # retain configs + end + end +end + +# determine where to find dispersion login information +if node['swift']['swift_secret_databag_name'].nil? + auth_user = node["swift"]["dispersion"]["auth_user"] + auth_key = node["swift"]["dispersion"]["auth_key"] +else + swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + auth_user = swift_secrets['dispersion_auth_user'] + auth_key = swift_secrets['dispersion_auth_key'] +end + +template "/etc/swift/dispersion.conf" do + source "dispersion.conf.erb" + owner "swift" + group "swift" + mode "0600" + variables("auth_url" => node["swift"]["auth_url"], + "auth_user" => auth_user, + "auth_key" => auth_key) +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/memcached.rb b/chef/cookbooks/openstack-object-storage/recipes/memcached.rb new file mode 100644 index 0000000..9cc533a --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/memcached.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: swift +# Recipe:: memcached +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "memcached" diff --git a/chef/cookbooks/openstack-object-storage/recipes/object-server.rb b/chef/cookbooks/openstack-object-storage/recipes/object-server.rb new file mode 100644 index 0000000..01bb152 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/object-server.rb @@ -0,0 +1,101 @@ +# +# Cookbook Name:: swift +# Recipe:: swift-object-server +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-object-storage::common" +include_recipe "openstack-object-storage::storage-common" +include_recipe "openstack-object-storage::disks" + +platform_options = node["swift"]["platform"] + +platform_options["object_packages"].each do |pkg| + package pkg do + action :install + options platform_options["override_options"] # retain configs + end +end + +# epel/f-17 missing init scripts for the non-major services. +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor updater replicator}.each do |svc| + template "/etc/systemd/system/openstack-swift-object-#{svc}.service" do + owner "root" + group "root" + mode "0644" + source "simple-systemd-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Object #{svc.capitalize}", + :user => "swift", + :exec => "/usr/bin/swift-object-#{svc} " + + "/etc/swift/object-server.conf" + }) + only_if { platform?(%w{fedora})} + end +end + +# TODO(breu): track against upstream epel packages to determine if this +# is still necessary +# https://bugzilla.redhat.com/show_bug.cgi?id=807170 +%w{auditor updater replicator}.each do |svc| + template "/etc/init.d/openstack-swift-object-#{svc}" do + owner "root" + group "root" + mode "0755" + source "simple-redhat-init-config.erb" + variables({ :description => "OpenStack Object Storage (swift) - " + + "Object #{svc.capitalize}", + :exec => "object-#{svc}" + }) + only_if { platform?(%w{redhat centos}) } + end +end + +%w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc| + service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"] + + service svc do + service_name service_name + provider platform_options["service_provider"] + # the default ubuntu provider uses invoke-rc.d, which apparently is + # status-illy broken in ubuntu + supports :status => false, :restart => true + action [:enable, :start] + only_if "[ -e /etc/swift/object-server.conf ] && [ -e /etc/swift/object.ring.gz ]" + end + +end + +template "/etc/swift/object-server.conf" do + source "object-server.conf.erb" + owner "swift" + group "swift" + mode "0600" + variables("bind_ip" => node["swift"]["network"]["object-bind-ip"], + "bind_port" => node["swift"]["network"]["object-bind-port"]) + + notifies :restart, "service[swift-object]", :immediately + notifies :restart, "service[swift-object-replicator]", :immediately + notifies :restart, "service[swift-object-updater]", :immediately + notifies :restart, "service[swift-object-auditor]", :immediately +end + +cron "swift-recon" do + minute "*/5" + command "swift-recon-cron /etc/swift/object-server.conf" + user "swift" +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb b/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb new file mode 100644 index 0000000..374e3c2 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/proxy-server.rb @@ -0,0 +1,116 @@ +# +# Cookbook Name:: swift +# Recipe:: proxy-server +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "openstack-object-storage::common" +include_recipe "openstack-object-storage::memcached" + +class Chef::Recipe + include IPUtils +end + +if node.run_list.expand(node.chef_environment).recipes.include?("swift::setup") + Chef::Log.info("I ran the swift::setup so I will use my own swift passwords") +else + setup = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup") + if setup.length == 0 + Chef::Application.fatal! "You must have run the swift::setup recipe (on this or another node) before running the swift::proxy recipe on this node" + elsif setup.length == 1 + Chef::Log.info "Found swift::setup node: #{setup[0].name}" + node.set["swift"]["service_pass"] = setup[0]["swift"]["service_pass"] + elsif setup.length >1 + Chef::Application.fatal! "You have multiple nodes in your environment that have run swift-setup, and that is not allowed" + end +end + +platform_options = node["swift"]["platform"] + +# install platform-specific packages +platform_options["proxy_packages"].each do |pkg| + package pkg do + action :install + options platform_options["override_options"] + end +end + +package "python-swauth" do + action :install + only_if { node["swift"]["authmode"] == "swauth" } +end + +package "python-swift-informant" do + action :install + only_if { node["swift"]["use_informant"] } +end + +package "python-keystone" do + action :install + only_if { node["swift"]["authmode"] == "keystone" } +end + +directory "/var/cache/swift" do + owner "swift" + group "swift" + mode 00700 +end + +swift_proxy_service = platform_options["service_prefix"] + "swift-proxy" + platform_options["service_suffix"] +service "swift-proxy" do + # openstack-swift-proxy.service on fedora-17, swift-proxy on ubuntu + service_name swift_proxy_service + provider platform_options["service_provider"] + supports :status => true, :restart => true + action [ :enable, :start ] + only_if "[ -e /etc/swift/proxy-server.conf ] && [ -e /etc/swift/object.ring.gz ]" +end + +# use localhost when using chef solo otherwise, include all memcache +# servers from all known proxies +if Chef::Config[:solo] + memcache_servers = [ "127.0.0.1:11211" ] +else + memcache_servers = [] + proxy_nodes = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-proxy-server") + proxy_nodes.each do |proxy| + proxy_ip = locate_ip_in_cidr(node["swift"]["network"]["proxy-cidr"], proxy) + next if not proxy_ip # skip nil ips so we dont break the config + server_str = "#{proxy_ip}:11211" + memcache_servers << server_str unless memcache_servers.include?(server_str) + end +end + +# determine authkey to use +if node['swift']['swift_secret_databag_name'].nil? + authkey = node['swift']['authkey'] +else + swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name'] + authkey = swift_secrets['swift_authkey'] +end + +# create proxy config file +template "/etc/swift/proxy-server.conf" do + source "proxy-server.conf.erb" + owner "swift" + group "swift" + mode "0600" + variables("authmode" => node["swift"]["authmode"], + "bind_host" => node["swift"]["network"]["proxy-bind-ip"], + "bind_port" => node["swift"]["network"]["proxy-bind-port"], + "authkey" => authkey, + "memcache_servers" => memcache_servers) + notifies :restart, "service[swift-proxy]", :immediately +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb b/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb new file mode 100644 index 0000000..4131132 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/ring-repo.rb @@ -0,0 +1,183 @@ +# +# Cookbook Name:: swift +# Recipe:: ring-repo +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This recipe creates a git ring repository on the management node +# for purposes of ring synchronization +# + +platform_options = node["swift"]["platform"] +ring_options = node["swift"]["ring"] + +platform_options["git_packages"].each do |pkg| + package pkg do + action :install + end +end + +service "xinetd" do + supports :status => false, :restart => true + action [ :enable, :start ] + only_if { platform?(%w{centos redhat fedora}) } +end + +execute "create empty git repo" do + cwd "/tmp" + umask 022 + command "mkdir $$; cd $$; git init; echo \"backups\" \> .gitignore; git add .gitignore; git commit -m 'initial commit' --author='chef '; git push file:///#{platform_options["git_dir"]}/rings master" + user "swift" + action :nothing +end + +directory "git-directory" do + path "#{platform_options["git_dir"]}/rings" + owner "swift" + group "swift" + mode "0755" + recursive true + action :create +end + +execute "initialize git repo" do + cwd "#{platform_options["git_dir"]}/rings" + umask 022 + user "swift" + command "git init --bare && touch git-daemon-export-ok" + creates "#{platform_options["git_dir"]}/rings/config" + action :run + notifies :run, "execute[create empty git repo]", :immediately +end + +# epel/f-17 missing systemd-ified inits +# https://bugzilla.redhat.com/show_bug.cgi?id=737183 +template "/etc/systemd/system/git.service" do + owner "root" + group "root" + mode "0644" + source "simple-systemd-config.erb" + variables({ :description => "Git daemon service", + :user => "nobody", + :exec => "/usr/libexec/git-core/git-daemon " + + "--base-path=/var/lib/git --export-all --user-path=public_git" + + "--syslog --verbose" + }) + only_if { platform?(%w{fedora}) } +end + +case node["platform"] +when "centos","redhat","fedora" + service "git-daemon" do + service_name platform_options["git_service"] + action [ :enable ] + end +when "ubuntu","debian" + service "git-daemon" do + service_name platform_options["git_service"] + action [ :enable, :start ] + end +end + +cookbook_file "/etc/default/git-daemon" do + owner "root" + group "root" + mode "644" + source "git-daemon.default" + action :create + notifies :restart, "service[git-daemon]", :immediately + not_if { platform?(%w{fedora centos redhat}) } +end + +directory "/etc/swift/ring-workspace" do + owner "swift" + group "swift" + mode "0755" + action :create +end + +execute "checkout-rings" do + cwd "/etc/swift/ring-workspace" + command "git clone file://#{platform_options["git_dir"]}/rings" + user "swift" + creates "/etc/swift/ring-workspace/rings" +end + +[ "account", "container", "object" ].each do |ring_type| + + part_power = ring_options["part_power"] + min_part_hours = ring_options["min_part_hours"] + replicas = ring_options["replicas"] + + Chef::Log.info("Building initial ring #{ring_type} using part_power=#{part_power}, " + + "min_part_hours=#{min_part_hours}, replicas=#{replicas}") + execute "add #{ring_type}.builder" do + cwd "/etc/swift/ring-workspace/rings" + command "git add #{ring_type}.builder && git commit -m 'initial ring builders' --author='chef '" + user "swift" + action :nothing + end + + execute "create #{ring_type} builder" do + cwd "/etc/swift/ring-workspace/rings" + command "swift-ring-builder #{ring_type}.builder create #{part_power} #{replicas} #{min_part_hours}" + user "swift" + creates "/etc/swift/ring-workspace/rings/#{ring_type}.builder" + notifies :run, "execute[add #{ring_type}.builder]", :immediate + end +end + +bash "rebuild-rings" do + action :nothing + cwd "/etc/swift/ring-workspace/rings" + user "swift" + code <<-EOF + set -x + + # Should this be done? + git reset --hard + git clean -df + + ../generate-rings.sh + for d in object account container; do swift-ring-builder ${d}.builder; done + + add=0 + if test -n "$(find . -maxdepth 1 -name '*gz' -print -quit)" + then + git add *builder *gz + add=1 + else + git add *builder + add=1 + fi + if [ $add -ne 0 ] + then + git commit -m "Autobuild of rings on $(date +%Y%m%d) by Chef" --author="chef " + git push + fi + + EOF +end + +openstack_object_storage_ring_script "/etc/swift/ring-workspace/generate-rings.sh" do + owner "swift" + group "swift" + mode "0700" + ring_path "/etc/swift/ring-workspace/rings" + action :ensure_exists + notifies :run, "bash[rebuild-rings]", :immediate +end + diff --git a/chef/cookbooks/openstack-object-storage/recipes/rsync.rb b/chef/cookbooks/openstack-object-storage/recipes/rsync.rb new file mode 100644 index 0000000..4f0fbf4 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/rsync.rb @@ -0,0 +1,85 @@ +# +# Cookbook Name:: swift +# Recipe:: rsync +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +platform_options = node["swift"]["platform"] + +platform_options["rsync_packages"].each do |pkg| + package pkg do + action :install + options platform_options["override_options"] + end +end + +# epel/f-17 broken: https://bugzilla.redhat.com/show_bug.cgi?id=737710 +cookbook_file "/etc/systemd/system/rsync.service" do + owner "root" + group "root" + mode "0644" + source "rsync.service" + action :create + only_if { platform?(%w{fedora}) } +end + +# rhel based systems install rsync and run it with rsync. We don't want to do that +cookbook_file "/etc/init.d/rsyncd" do + owner "root" + group "root" + mode "0755" + source "rsync.init" + action :create + only_if { platform?(%w{centos redhat scientific}) } +end + +# FIXME: chicken and egg +case node["platform"] +when "centos","redhat","fedora" + # enable rsyncd + rsync_servicename = "rsyncd" + service "rsyncd" do + supports :status => false, :restart => true, :start => true, :stop => true + action [ :enable, :start ] + only_if "[ -f /etc/rsyncd.conf ]" + end + # disable rsync (the one via xinetd) + service "rsync" do + supports :status => false, :restart => false, :start => false, :stop => false + action [ :disable ] + end +when "ubuntu","debian" + rsync_servicename = "rsync" + service "rsync" do + supports :status => false, :restart => true + action [ :enable, :start ] + only_if "[ -f /etc/rsyncd.conf ]" + end +end + +template "/etc/rsyncd.conf" do + source "rsyncd.conf.erb" + mode "0644" + notifies :restart, "service[#{rsync_servicename}]", :immediately +end + +execute "enable rsync" do + command "sed -i 's/RSYNC_ENABLE=false/RSYNC_ENABLE=true/' /etc/default/rsync" + only_if "grep -q 'RSYNC_ENABLE=false' /etc/default/rsync" + notifies :restart, "service[rsync]", :immediately + action :run + not_if { platform?(%w{fedora centos redhat scientific}) } +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/setup.rb b/chef/cookbooks/openstack-object-storage/recipes/setup.rb new file mode 100644 index 0000000..7558914 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/setup.rb @@ -0,0 +1,58 @@ +# +# Cookbook Name:: swift +# Recipe:: setup +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "openstack-object-storage::common" + +# make sure we die if there are multiple swift-setups +if Chef::Config[:solo] + Chef::Application.fatal! "This recipe uses search. Chef Solo does not support search." +else + setup_role_count = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup").length + if setup_role_count > 1 + Chef::Application.fatal! "You can only have one node with the swift-setup role" + end +end + +unless node["swift"]["service_pass"] + Chef::Log.info("Running swift setup - setting swift passwords") +end + +platform_options = node["swift"]["platform"] + +# install platform-specific packages +platform_options["proxy_packages"].each do |pkg| + package pkg do + action :upgrade + options platform_options["override_options"] + end +end + +package "python-swauth" do + action :upgrade + only_if { node["swift"]["authmode"] == "swauth" } +end + +package "python-swift-informant" do + action :upgrade + only_if { node["swift"]["use_informant"] } +end + +package "python-keystone" do + action :upgrade + only_if { node["swift"]["authmode"] == "keystone" } +end diff --git a/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb b/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb new file mode 100644 index 0000000..4fb834f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/recipes/storage-common.rb @@ -0,0 +1,41 @@ +# +# Cookbook Name:: swift +# Recipe:: storage-common +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-object-storage::rsync" + +template "/etc/swift/drive-audit.conf" do + source "drive-audit.conf.erb" + owner "swift" + group "swift" + mode "0600" +end + +cron "drive-audit" do + hour node["swift"]["audit_hour"] + minute "10" + command "swift-drive-audit /etc/swift/drive-audit.conf" +end + +directory "/var/cache/swift" do + group "swift" + owner "swift" + recursive true + action :create + mode 00700 +end diff --git a/chef/cookbooks/openstack-object-storage/resources/disk.rb b/chef/cookbooks/openstack-object-storage/resources/disk.rb new file mode 100644 index 0000000..ad9023f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/resources/disk.rb @@ -0,0 +1,44 @@ +# +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: andi abes +# + +=begin + Ensure that a disk's partition table matches expectations. + Sample use: + + openstack_object_storage_disk "/dev/sdb" do + part( + {[:type => "xfs", :size =>swift_disk::ONE_GIG*4 ], + [:type => "xfs", :size =>swift_disk::remaining}) + action :ensure_exists + end + +=end + +actions :ensure_exists + +def initialize(*args) + super + @action = :ensure_exists +end + +attribute :name, :kind_of => String +attribute :size, :kind_of => Integer +attribute :blocks, :kind_of => Integer +attribute :device, :kind_of => String +attribute :part, :kind_of => Array +attribute :status, :kind_of => Symbol diff --git a/chef/cookbooks/openstack-object-storage/resources/mounts.rb b/chef/cookbooks/openstack-object-storage/resources/mounts.rb new file mode 100644 index 0000000..d8c7443 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/resources/mounts.rb @@ -0,0 +1,71 @@ +# +# Cookbook Name:: swift +# Resource:: mounts +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# + +=begin + Ensure that swift mounts are strongly enforced. This + will ensure specified drives are mounted, and unspecified + drives are not mounted. In addition, if there is a stale + mountpoint (from disk failure, maybe?), then that mountpoint + will try to be unmounted + + Sample use: + + openstack_object_storage_mounts "/srv/node" do + devices [ "sdb1", "sdc1" ] + action :ensure_exists + ip "10.1.1.1" + end + + It will force mounts based on fs uuid (mangled to remove + dashes) and return a structure that describes the disks + mounted. + + As this is expected to be consumed for the purposes of + swift, the ip address should be the address that gets + embedded into the ring (i.e. the listen port of the storage server) + + Example return structure: + + { "2a9452c5-d929-43d9-9631-4340ace45279": { + "device": "sdb1", + "ip": "10.1.1.1", + "mounted": "true", + "mountpoint": "2a9452c5d92943d996314340ace45279", + "size": 1022 (in 1k increments) + "uuid": "2a9452c5-d929-43d9-9631-4340ace45279" + }, + ... + } + +=end + +actions :ensure_exists + +def initialize(*args) + super + @action = :ensure_exists +end + +attribute :name, :kind_of => String +attribute :devices, :kind_of => Array +attribute :ip, :kind_of => String, :default => "127.0.0.1" +attribute :publish_attributes, :kind_of => String, :default => nil +attribute :format, :kind_of => String, :default => "xfs" diff --git a/chef/cookbooks/openstack-object-storage/resources/ring_script.rb b/chef/cookbooks/openstack-object-storage/resources/ring_script.rb new file mode 100644 index 0000000..9a6ad30 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/resources/ring_script.rb @@ -0,0 +1,44 @@ +# +# Copyright 2012, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Ron Pedde +# + +=begin + Build a proposed ring-building script + Sample use: + + openstack_object_storage_ring_script "/tmp/build-rings.sh" do + owner "root" + group "swift" + mode "0700" + ring_path "/etc/swift/ring-workspace" + action :ensure_exists + end + +=end + +actions :ensure_exists + +def initialize(*args) + super + @action = :ensure_exists +end + +attribute :name, :kind_of => String +attribute :owner, :kind_of => String, :default => "root" +attribute :group, :kind_of => String, :default => "root" +attribute :mode, :kind_of => String, :default => "0600" +attribute :ring_path, :kind_of => String, :default => "/etc/swift" diff --git a/chef/cookbooks/openstack-object-storage/run_tests.bash b/chef/cookbooks/openstack-object-storage/run_tests.bash new file mode 100755 index 0000000..e00ccf8 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/run_tests.bash @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# A script to run tests locally before committing. + +set -e + +COOKBOOK=$(awk '/^name/ {print $NF}' metadata.rb |tr -d \"\') +if [ -z $COOKBOOK ]; then + echo "Cookbook name not defined in metadata.rb" +    exit 1 +fi + +BUNDLE_PATH=${BUNDLE_PATH:-.bundle} +BERKSHELF_PATH=${BERKSHELF_PATH:-.cookbooks} + +echo "Using bundle path: $BUNDLE_PATH" +echo "Using berkshelf path: $BERKSHELF_PATH" + +bundle install --path=${BUNDLE_PATH} +bundle exec berks install --path=${BERKSHELF_PATH} +bundle exec rspec ${BERKSHELF_PATH}/${COOKBOOK} +bundle exec foodcritic -f any -t ~FC003 -t ~FC023 ${BERKSHELF_PATH}/${COOKBOOK} + diff --git a/chef/cookbooks/openstack-object-storage/spec/account_spec.rb b/chef/cookbooks/openstack-object-storage/spec/account_spec.rb new file mode 100644 index 0000000..6a796a5 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/account_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe 'openstack-object-storage::account-server' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['network']['account-bind-ip'] = '10.0.0.1' + @node.set['swift']['network']['account-bind-port'] = '8080' + @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" + @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] + + # mock out an interface on the storage node + @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + + @chef_run.converge "openstack-object-storage::account-server" + end + + it "installs swift account packages" do + expect(@chef_run).to install_package "swift-account" + end + + it "installs swiftclient package" do + expect(@chef_run).to install_package "python-swiftclient" + end + + it "starts swift account services on boot" do + %w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc| + expect(@chef_run).to set_service_to_start_on_boot svc + end + end + + describe "/etc/swift/account-server.conf" do + + before do + @file = @chef_run.template "/etc/swift/account-server.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/common_spec.rb b/chef/cookbooks/openstack-object-storage/spec/common_spec.rb new file mode 100644 index 0000000..27bb021 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/common_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe 'openstack-object-storage::common' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['platform_family'] = "debian" + @node.set['lsb']['codename'] = "precise" + @node.set['swift']['release'] = "folsom" + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['git_builder_ip'] = '10.0.0.10' + + # TODO: this does not work + # ::Chef::Log.should_receive(:info).with("chefspec: precise-updates/folsom") + + @chef_run.converge "openstack-object-storage::common" + end + + + it 'should set syctl paramaters' do + # N.B. we could examine chef log + pending "TODO: right now theres no way to do lwrp and test for this" + end + + it 'installs git package for ring management' do + expect(@chef_run).to install_package "git" + end + + describe "/etc/swift" do + + before do + @file = @chef_run.directory "/etc/swift" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "700" + end + + end + + describe "/etc/swift/swift.conf" do + + before do + @file = @chef_run.file "/etc/swift/swift.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "700" + end + + end + + describe "/etc/swift/pull-rings.sh" do + + before do + @file = @chef_run.template "/etc/swift/pull-rings.sh" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "700" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/container_spec.rb b/chef/cookbooks/openstack-object-storage/spec/container_spec.rb new file mode 100644 index 0000000..f56932d --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/container_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe 'openstack-object-storage::container-server' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['network']['container-bind-ip'] = '10.0.0.1' + @node.set['swift']['network']['container-bind-port'] = '8080' + @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" + @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] + + # mock out an interface on the storage node + @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + + @chef_run.converge "openstack-object-storage::container-server" + end + + it "installs swift container packages" do + expect(@chef_run).to install_package "swift-container" + end + + it "starts swift container services on boot" do + %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc| + expect(@chef_run).to set_service_to_start_on_boot svc + end + end + + describe "/etc/swift/container-server.conf" do + + before do + @file = @chef_run.template "/etc/swift/container-server.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb b/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb new file mode 100644 index 0000000..03e7ccc --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/disks_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe 'openstack-object-storage::disks' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['platform_family'] = "debian" + @node.set['lsb']['codename'] = "precise" + @node.set['swift']['release'] = "folsom" + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['git_builder_ip'] = '10.0.0.10' + @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" + @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] + + # mock out an interface on the storage node + @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + + @chef_run.converge "openstack-object-storage::disks" + end + + it 'installs xfs progs package' do + expect(@chef_run).to install_package "xfsprogs" + end + + it 'installs parted package' do + expect(@chef_run).to install_package "parted" + end + + end + + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/management_spec.rb b/chef/cookbooks/openstack-object-storage/spec/management_spec.rb new file mode 100644 index 0000000..c7724b6 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/management_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe 'openstack-object-storage::management-server' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + + @chef_run.converge "openstack-object-storage::management-server" + end + + it "installs swift swauth package" do + expect(@chef_run).to install_package "swauth" + end + + describe "/etc/swift/dispersion.conf" do + + before do + @file = @chef_run.template "/etc/swift/dispersion.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/object_spec.rb b/chef/cookbooks/openstack-object-storage/spec/object_spec.rb new file mode 100644 index 0000000..b7c75a3 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/object_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'openstack-object-storage::object-server' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['network']['object-bind-ip'] = '10.0.0.1' + @node.set['swift']['network']['object-bind-port'] = '8080' + @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]" + @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/", + "File.exist?('/dev/' + candidate)", + "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')", + "not info.has_key?('removable') or info['removable'] == 0.to_s"] + + # mock out an interface on the storage node + @node.set["network"] = MOCK_NODE_NETWORK_DATA['network'] + + @chef_run.converge "openstack-object-storage::object-server" + end + + it "installs swift packages" do + expect(@chef_run).to install_package "swift-object" + end + + it "starts swift object services on boot" do + %w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc| + expect(@chef_run).to set_service_to_start_on_boot svc + end + end + + describe "/var/spool/crontab/root" do + + it "template contents" do + pending "TODO: check for recon script" + end + + end + + describe "/etc/swift/object-server.conf" do + + before do + @file = @chef_run.template "/etc/swift/object-server.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb b/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb new file mode 100644 index 0000000..18d6f1f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/proxy_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe 'openstack-object-storage::proxy-server' do + + #-------------- + # UBUNTU + #-------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['network']['proxy-bind-ip'] = '10.0.0.1' + @node.set['swift']['network']['proxy-bind-port'] = '8080' + @chef_run.converge "openstack-object-storage::proxy-server" + end + + it "installs memcache python packages" do + expect(@chef_run).to install_package "python-memcache" + end + + it "installs swift packages" do + expect(@chef_run).to install_package "swift-proxy" + end + + it "installs swauth package if swauth is selected" do + expect(@chef_run).to install_package "python-swauth" + end + + it "starts swift-proxy on boot" do + expect(@chef_run).to set_service_to_start_on_boot "swift-proxy" + end + + describe "/etc/swift/proxy-server.conf" do + + before do + @file = @chef_run.template "/etc/swift/proxy-server.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb b/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb new file mode 100644 index 0000000..7f75501 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/ring-repo_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe 'openstack-object-storage::ring-repo' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['platform_family'] = "debian" + @node.set['lsb']['codename'] = "precise" + @node.set['swift']['release'] = "folsom" + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['git_builder_ip'] = '10.0.0.10' + @chef_run.converge "openstack-object-storage::ring-repo" + end + + it 'installs git package for ring management' do + expect(@chef_run).to install_package "git-daemon-sysvinit" + end + + it "starts xinetd services on boot" do + %w{xinetd}.each do |svc| + expect(@chef_run).to set_service_to_start_on_boot svc + end + end + + describe "/etc/swift/ring-workspace/generate-rings.sh" do + + it "gets installed" do + pending "TODO: determine some way to ensure this LWRP script gets created" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb b/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb new file mode 100644 index 0000000..bc894ce --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/rsync_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'openstack-object-storage::rsync' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['platform_family'] = "debian" + @node.set['lsb']['codename'] = "precise" + @node.set['swift']['release'] = "folsom" + @node.set['swift']['authmode'] = 'swauth' + @node.set['swift']['git_builder_ip'] = '10.0.0.10' + @chef_run.converge "openstack-object-storage::rsync" + end + + it 'installs git package for ring management' do + expect(@chef_run).to install_package "rsync" + end + + it "starts rsync service on boot" do + %w{rsync}.each do |svc| + expect(@chef_run).to set_service_to_start_on_boot svc + end + end + + describe "/etc/rsyncd.conf" do + + before do + @file = @chef_run.template "/etc/rsyncd.conf" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "644" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb b/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb new file mode 100644 index 0000000..a70cbcc --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/spec_helper.rb @@ -0,0 +1,49 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::REDHAT_OPTS = { + :platform => "redhat", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +MOCK_NODE_NETWORK_DATA = + { + "ipaddress" => '10.0.0.2', + "fqdn" => 'localhost.localdomain', + "hostname" => 'localhost', + "network" => { + "default_interface" => "eth0", + "interfaces" => { + "eth0" => { + "addresses" => { + "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"}, + "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"}, + "08:00:27:CA:AB:08" => {"family" => "lladdr"} + }, + }, + "lo" => { + "addresses" => { + "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"}, + "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"} + }, + }, + }, + } + } + +def swift_stubs + # create mock cluster + n = Chef::Node.new() + n.name('manager') + n.default_attrs = { + "swift" => { + "service_pass" => "foobar" + } + } + Chef::Recipe.any_instance.stub(:search).with(:node, 'chef_environment:_default AND roles:swift-setup').and_return([n]) +end diff --git a/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb b/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb new file mode 100644 index 0000000..50a3ef8 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/spec/storage-common_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'openstack-object-storage::storage-common' do + + #------------------- + # UBUNTU + #------------------- + + describe "ubuntu" do + + before do + swift_stubs + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @node = @chef_run.node + @node.set['lsb']['code'] = 'precise' + @node.set['swift']['authmode'] = 'swauth' + @chef_run.converge "openstack-object-storage::storage-common" + end + + describe "/var/cache/swift" do + + before do + @file = @chef_run.directory "/var/cache/swift" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "700" + end + + end + + describe "/etc/swift/drive-audit.conf" do + + before do + @file = @chef_run.template "/etc/swift/drive-audit.conf" + end + + it "has proper owner" do + expect(@file).to be_owned_by "swift", "swift" + end + + it "has proper modes" do + expect(sprintf("%o", @file.mode)).to eq "600" + end + + it "template contents" do + pending "TODO: implement" + end + + end + + end + +end diff --git a/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb new file mode 100644 index 0000000..6ba2498 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/account-server.conf.erb @@ -0,0 +1,78 @@ +[DEFAULT] +# bind_ip = 0.0.0.0 +# bind_port = 6002 +# backlog = 4096 +# workers = 1 +# user = swift +# swift_dir = /etc/swift +# devices = /srv/node +# mount_check = true +# You can specify default log routing here if you want: +# log_name = swift +# log_facility = LOG_LOCAL0 +# log_level = INFO +##### +bind_ip = <%= @bind_ip %> +bind_port = <%= @bind_port %> +workers = 10 +<% if node[:swift][:enable_statistics] -%> +log_statsd_host = localhost +log_statsd_port = 8125 +log_statsd_default_sample_rate = 1 +log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +<% end %> + +[pipeline:main] +pipeline = account-server + +[app:account-server] +use = egg:swift#account +# You can override the default log routing for this app here: +# set log_name = account-server +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_requests = True + +[account-replicator] +# You can override the default log routing for this app here (don't use set!): +# log_name = account-replicator +# log_facility = LOG_LOCAL0 +# log_level = INFO +# vm_test_mode = no +# log_facility = LOG_LOCAL0 +# log_level = INFO +# per_diff = 1000 +# max_diffs = 100 +# concurrency = 8 +# interval = 30 +# How long without an error before a node's error count is reset. This will +# also be how long before a node is reenabled after suppression is triggered. +# error_suppression_interval = 60 +# How many errors can accumulate before a node is temporarily ignored. +# error_suppression_limit = 10 +# node_timeout = 10 +# conn_timeout = 0.5 +# The replicator also performs reclamation +# reclaim_age = 86400 + +[account-auditor] +# You can override the default log routing for this app here (don't use set!): +# log_name = account-auditor +# log_facility = LOG_LOCAL0 +# log_level = INFO +# Will audit, at most, 1 account per device per interval +# interval = 1800 +# log_facility = LOG_LOCAL0 +# log_level = INFO + +[account-reaper] +# You can override the default log routing for this app here (don't use set!): +# log_name = account-reaper +# log_facility = LOG_LOCAL0 +# log_level = INFO +# concurrency = 25 +# interval = 3600 +# node_timeout = 10 +# conn_timeout = 0.5 +# log_facility = LOG_LOCAL0 +# log_level = INFO diff --git a/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb new file mode 100644 index 0000000..144214f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/container-server.conf.erb @@ -0,0 +1,88 @@ +[DEFAULT] +# bind_ip = 0.0.0.0 +# bind_port = 6001 +# backlog = 4096 +# workers = 1 +# user = swift +# swift_dir = /etc/swift +# devices = /srv/node +# mount_check = true +# This is a comma separated list of hosts allowed in the X-Container-Sync-To +# field for containers. +# allowed_sync_hosts = 127.0.0.1 +# You can specify default log routing here if you want: +# log_name = swift +# log_facility = LOG_LOCAL0 +# log_level = INFO +#### +bind_ip = <%= @bind_ip %> +bind_port = <%= @bind_port %> +workers = 10 +<% if node[:swift][:enable_statistics] -%> +log_statsd_host = localhost +log_statsd_port = 8125 +log_statsd_default_sample_rate = 1 +log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +<% end %> + +[pipeline:main] +pipeline = container-server + +[app:container-server] +use = egg:swift#container +# You can override the default log routing for this app here: +# set log_name = container-server +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_requests = True +# node_timeout = 3 +# conn_timeout = 0.5 + +[container-replicator] +# You can override the default log routing for this app here (don't use set!): +# log_name = container-replicator +# log_facility = LOG_LOCAL0 +# log_level = INFO +# vm_test_mode = no +# per_diff = 1000 +# max_diffs = 100 +# concurrency = 8 +# interval = 30 +# node_timeout = 10 +# conn_timeout = 0.5 +# The replicator also performs reclamation +# reclaim_age = 604800 + +[container-updater] +# You can override the default log routing for this app here (don't use set!): +# log_name = container-updater +# log_facility = LOG_LOCAL0 +# log_level = INFO +# interval = 300 +# concurrency = 4 +# node_timeout = 3 +# conn_timeout = 0.5 +# slowdown will sleep that amount between containers +# slowdown = 0.01 +# Seconds to suppress updating an account that has generated an error +# account_suppression_time = 60 + +[container-auditor] +# You can override the default log routing for this app here (don't use set!): +# log_name = container-auditor +# log_facility = LOG_LOCAL0 +# log_level = INFO +# Will audit, at most, 1 container per device per interval +# interval = 1800 + +[container-sync] +# You can override the default log routing for this app here (don't use set!): +# log_name = container-sync +# log_facility = LOG_LOCAL0 +# log_level = INFO +# If you need to use an HTTP Proxy, set it here; defaults to no proxy. +# sync_proxy = http://127.0.0.1:8888 +# Will sync, at most, each container once per interval +# interval = 300 +# Maximum amount of time to spend syncing each container per pass +# container_time = 60 diff --git a/chef/cookbooks/openstack-object-storage/templates/default/dispersion.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/dispersion.conf.erb new file mode 100644 index 0000000..3a1cd87 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/dispersion.conf.erb @@ -0,0 +1,13 @@ +[dispersion] +auth_url = <%= @auth_url %> +auth_user = <%= @auth_user %> +auth_key = <%= @auth_key %> +auth_version = 1.0 +endpoint_type = publicURL +swift_dir = /etc/swift +dispersion_coverage = 5 +retries = 5 +concurrency = 25 +container_report = yes +object_report = yes +dump_json = no diff --git a/chef/cookbooks/openstack-object-storage/templates/default/drive-audit.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/drive-audit.conf.erb new file mode 100644 index 0000000..ddde011 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/drive-audit.conf.erb @@ -0,0 +1,5 @@ +[drive-audit] +log_facility = LOG_LOCAL0 +log_level = INFO +device_dir = /srv/node +minutes = 60 diff --git a/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb new file mode 100644 index 0000000..ea55dd8 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/object-server.conf.erb @@ -0,0 +1,104 @@ +[DEFAULT] +# bind_ip = 0.0.0.0 +# bind_port = 6000 +# backlog = 4096 +# workers = 1 +# user = swift +# swift_dir = /etc/swift +# devices = /srv/node +# mount_check = true +# expiring_objects_container_divisor = 86400 +# You can specify default log routing here if you want: +# log_name = swift +# log_facility = LOG_LOCAL0 +# log_level = INFO +##### +bind_ip = <%= @bind_ip %> +bind_port = <%= @bind_port %> +workers = 10 +<% if node[:swift][:enable_statistics] -%> +log_statsd_host = localhost +log_statsd_port = 8125 +log_statsd_default_sample_rate = 1 +log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +<% end %> + +[pipeline:main] +pipeline = recon object-server + +[app:object-server] +use = egg:swift#object +# You can override the default log routing for this app here: +# set log_name = object-server +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_requests = True +# node_timeout = 3 +# conn_timeout = 0.5 +# network_chunk_size = 65536 +# disk_chunk_size = 65536 +# max_upload_time = 86400 +# slow = 1 +# on PUTs, sync data every n MB +# mb_per_sync = 512 +# Comma separated list of headers that can be set in metadata on an object. +# This list is in addition to X-Object-Meta-* headers and cannot include +# Content-Type, etag, Content-Length, or deleted +# allowed_headers = Content-Disposition, Content-Encoding, X-Delete-At, X-Object-Manifest + +[filter:recon] +use = egg:swift#recon +recon_cache_path = /var/cache/swift + +[object-replicator] +# You can override the default log routing for this app here (don't use set!): +# log_name = object-replicator +# log_facility = LOG_LOCAL0 +# log_level = INFO +# vm_test_mode = no +# daemonize = on +# run_pause = 30 +# concurrency = 1 +# stats_interval = 300 +# max duration of a partition rsync +# rsync_timeout = 900 +# passed to rsync for io op timeout +# rsync_io_timeout = 30 +# max duration of an http request +# http_timeout = 60 +# attempts to kill all workers if nothing replicates for lockup_timeout seconds +# lockup_timeout = 1800 +# The replicator also performs reclamation +# reclaim_age = 604800 +# enable logging of replication stats for recon +# recon_enable = no +# recon_cache_path = /var/cache/swift +##### +recon_enable = yes +recon_cache_path = /var/cache/swift +concurrency = 2 + + +[object-updater] +# You can override the default log routing for this app here (don't use set!): +# log_name = object-updater +# log_facility = LOG_LOCAL0 +# log_level = INFO +# interval = 300 +# concurrency = 1 +# node_timeout = 10 +# conn_timeout = 0.5 +# slowdown will sleep that amount between objects +# slowdown = 0.01 +concurrency = 2 + +[object-auditor] +# You can override the default log routing for this app here (don't use set!): +# log_name = object-auditor +# log_facility = LOG_LOCAL0 +# log_level = INFO +# files_per_second = 20 +# bytes_per_second = 10000000 +# log_time = 3600 +# zero_byte_files_per_second = 50 + diff --git a/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb new file mode 100644 index 0000000..9576af4 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/proxy-server.conf.erb @@ -0,0 +1,262 @@ +<% + +case @authmode +when "keystone" + pipeline="authtoken keystoneauth" +when "swauth" + pipeline="swauth" +end + +account_management=false +if node[:roles].include?("swift-management-server") and node[:swift][:authmode] == "swauth" then + account_management="true" +end +-%> +# This file is managed by chef. Do not edit it. +# +# Cluster info: +# Auth mode: <%= node[:swift][:authmode] %> +# Management server: <%= node[:roles].include?("swift-management-server") %> +# Account management enabled: <%= account_management %> +# Auth pipeline: <%= pipeline %> + +[DEFAULT] +# bind_ip = 0.0.0.0 +# bind_port = 8080 +# backlog = 4096 +# swift_dir = /etc/swift +# workers = 1 +# user = swift +# cert_file = /etc/swift/proxy.crt +# key_file = /etc/swift/proxy.key +# expiring_objects_container_divisor = 86400 +# You can specify default log routing here if you want: +# log_name = swift +# log_facility = LOG_LOCAL0 +# log_level = INFO +###### +workers = <%= [ node[:cpu][:total] - 1, 1 ].max %> +bind_ip = <%= @bind_host %> +bind_port = <%= @bind_port %> +<% if node[:swift][:enable_statistics] -%> +log_statsd_host = localhost +log_statsd_port = 8125 +log_statsd_default_sample_rate = 1 +log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %> +<% end %> + + +[pipeline:main] +pipeline = catch_errors healthcheck cache ratelimit <%= pipeline %> proxy-logging proxy-server + +[app:proxy-server] +use = egg:swift#proxy +# You can override the default log routing for this app here: +# set log_name = proxy-server +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set access_log_name = proxy-server +# set access_log_facility = LOG_LOCAL0 +# set access_log_level = INFO +# set log_headers = False +# recheck_account_existence = 60 +# recheck_container_existence = 60 +# object_chunk_size = 8192 +# client_chunk_size = 8192 +# node_timeout = 10 +# client_timeout = 60 +# conn_timeout = 0.5 +# How long without an error before a node's error count is reset. This will +# also be how long before a node is reenabled after suppression is triggered. +# error_suppression_interval = 60 +# How many errors can accumulate before a node is temporarily ignored. +# error_suppression_limit = 10 +# If set to 'true' any authorized user may create and delete accounts; if +# 'false' no one, even authorized, can. +# allow_account_management = false +# Set object_post_as_copy = false to turn on fast posts where only the metadata +# changes are stored anew and the original data file is kept in place. This +# makes for quicker posts; but since the container metadata isn't updated in +# this mode, features like container sync won't be able to sync posts. +# object_post_as_copy = true +# If set to 'true' authorized accounts that do not yet exist within the Swift +# cluster will be automatically created. +# account_autocreate = false +###### +# +# N.B. ideally allow_account_management would only be set on the +# management server, but swauth will delete using the cluster url +# and not the local url +# allow_account_managemnet = <%= account_management %> +allow_account_management = true + +<% if @authmode == "keystone" -%> +account_autocreate = true +<% end %> + +<% if @authmode == "swauth" -%> +[filter:swauth] +use = egg:swauth#swauth +# set log_name = swauth +# super_admin_key = +###### +<% if account_management -%> +super_admin_key = <%= @authkey %> +default_swift_cluster = local#<%= node[:swift][:swift_url] %>#<%= node[:swift][:swauth_url] %> +<% else %> +default_swift_cluster = local#<%= node[:swift][:swift_url] %> +<% end %> +<% end %> + +[filter:healthcheck] +use = egg:swift#healthcheck +# You can override the default log routing for this filter here: +# set log_name = healthcheck +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False + +[filter:cache] +use = egg:swift#memcache +# You can override the default log routing for this filter here: +# set log_name = cache +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False +# Default for memcache_servers is to try to read the property from +# memcache.conf (see memcache.conf-sample) or lacking that file, it will +# default to the value below. You can specify multiple servers separated with +# commas, as in: 10.1.2.3:11211,10.1.2.4:11211 +# memcache_servers = 127.0.0.1:11211 +##### +memcache_servers = <%= @memcache_servers.join(",") %> + +[filter:ratelimit] +use = egg:swift#ratelimit +# You can override the default log routing for this filter here: +# set log_name = ratelimit +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False +# clock_accuracy should represent how accurate the proxy servers' system clocks +# are with each other. 1000 means that all the proxies' clock are accurate to +# each other within 1 millisecond. No ratelimit should be higher than the +# clock accuracy. +# clock_accuracy = 1000 +# max_sleep_time_seconds = 60 +# log_sleep_time_seconds of 0 means disabled +# log_sleep_time_seconds = 0 +# allows for slow rates (e.g. running up to 5 sec's behind) to catch up. +# rate_buffer_seconds = 5 +# account_ratelimit of 0 means disabled +# account_ratelimit = 0 + +# these are comma separated lists of account names +# account_whitelist = a,b +# account_blacklist = c,d + +# with container_limit_x = r +# for containers of size x limit requests per second to r. The container +# rate will be linearly interpolated from the values given. With the values +# below, a container of size 5 will get a rate of 75. +# container_ratelimit_0 = 100 +# container_ratelimit_10 = 50 +# container_ratelimit_50 = 20 + +[filter:domain_remap] +use = egg:swift#domain_remap +# You can override the default log routing for this filter here: +# set log_name = domain_remap +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False +# storage_domain = example.com +# path_root = v1 +# reseller_prefixes = AUTH + +[filter:catch_errors] +use = egg:swift#catch_errors +# You can override the default log routing for this filter here: +# set log_name = catch_errors +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False + +[filter:cname_lookup] +# Note: this middleware requires python-dnspython +use = egg:swift#cname_lookup +# You can override the default log routing for this filter here: +# set log_name = cname_lookup +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set log_headers = False +# storage_domain = example.com +# lookup_depth = 1 + +# Note: Put staticweb just after your auth filter(s) in the pipeline +[filter:staticweb] +use = egg:swift#staticweb +# Seconds to cache container x-container-meta-web-* header values. +# cache_timeout = 300 +# You can override the default log routing for this filter here: +# set log_name = staticweb +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set access_log_name = staticweb +# set access_log_facility = LOG_LOCAL0 +# set access_log_level = INFO +# set log_headers = False + +# Note: Put tempurl just before your auth filter(s) in the pipeline +[filter:tempurl] +use = egg:swift#tempurl +# +# The headers to remove from incoming requests. Simply a whitespace delimited +# list of header names and names can optionally end with '*' to indicate a +# prefix match. incoming_allow_headers is a list of exceptions to these +# removals. +# incoming_remove_headers = x-timestamp +# +# The headers allowed as exceptions to incoming_remove_headers. Simply a +# whitespace delimited list of header names and names can optionally end with +# '*' to indicate a prefix match. +# incoming_allow_headers = +# +# The headers to remove from outgoing responses. Simply a whitespace delimited +# list of header names and names can optionally end with '*' to indicate a +# prefix match. outgoing_allow_headers is a list of exceptions to these +# removals. +# outgoing_remove_headers = x-object-meta-* +# +# The headers allowed as exceptions to outgoing_remove_headers. Simply a +# whitespace delimited list of header names and names can optionally end with +# '*' to indicate a prefix match. +# outgoing_allow_headers = x-object-meta-public-* + +# Note: Put formpost just before your auth filter(s) in the pipeline +[filter:formpost] +use = egg:swift#formpost + +[filter:keystoneauth] +operator_roles = Member,admin +use = egg:swift#keystoneauth + +[filter:proxy-logging] +use = egg:swift#proxy_logging +# access_log_name = proxy +# access_log_facility = LOG_LOCAL0 +# access_log_level = INFO +# access_log_address = /dev/log +# If set, access_log_udp_host will override access_log_address +# access_log_udp_host = +# access_log_udp_port = 514 +# You can use log_statsd_* from [DEFAULT] or override them here: +# access_log_statsd_host = localhost +# access_log_statsd_port = 8125 +# access_log_statsd_default_sample_rate = 1 +# access_log_statsd_metric_prefix = +# access_log_headers = False +# What HTTP methods are allowed for StatsD logging (comma-sep); request methods +# not in this list will have "BAD_METHOD" for the portion of the metric. +# log_statsd_valid_http_methods = GET,HEAD,POST,PUT,DELETE,COPY + diff --git a/chef/cookbooks/openstack-object-storage/templates/default/pull-rings.sh.erb b/chef/cookbooks/openstack-object-storage/templates/default/pull-rings.sh.erb new file mode 100644 index 0000000..5e4f57c --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/pull-rings.sh.erb @@ -0,0 +1,29 @@ +#!/bin/bash + +# this has to be run as root to restart the services... + +if [ ! -d /etc/swift/rings ] || [ ! -e /etc/swift/rings/.git/config ]; then + rm -rf /etc/swift/rings + git clone git://<%= @builder_ip %>/rings /etc/swift/rings +fi + +cd /etc/swift/rings +git reset --hard +git clean -df +git pull + +[ -e /etc/swift/rings ] && chown -R swift: /etc/swift/rings + +for d in object account container; do + if [ -e /etc/swift/rings/${d}.ring.gz ]; then + if [ ! -e ../${d}.ring.gz ] || [ "$(md5sum ${d}.ring.gz | cut -f1 -d' ')" != "$(md5sum ../${d}.ring.gz | cut -f1 -d' ')" ]; then + cp ${d}.ring.gz ../${d}.ring.new + chown swift: ../${d}.ring.new + mv ../${d}.ring.new ../${d}.ring.gz + + if [ -e /etc/swift/${d}-server.conf ]; then + service <%= @service_prefix %>swift-${d}-replicator restart + fi + fi + fi +done diff --git a/chef/cookbooks/openstack-object-storage/templates/default/rsyncd.conf.erb b/chef/cookbooks/openstack-object-storage/templates/default/rsyncd.conf.erb new file mode 100644 index 0000000..5bd8526 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/rsyncd.conf.erb @@ -0,0 +1,24 @@ +uid = swift +gid = swift + +log file = /var/log/rsyncd.log +pid file = /var/run/rsyncd.pid +address = 0.0.0.0 + +[account] +max connections = 10 +path = /srv/node/ +read only = false +lock file = /var/lock/account.lock + +[container] +max connections = 10 +path = /srv/node/ +read only = false +lock file = /var/lock/container.lock + +[object] +max connections = 10 +path = /srv/node/ +read only = false +lock file = /var/lock/object.lock diff --git a/chef/cookbooks/openstack-object-storage/templates/default/simple-redhat-init-config.erb b/chef/cookbooks/openstack-object-storage/templates/default/simple-redhat-init-config.erb new file mode 100644 index 0000000..28c002f --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/simple-redhat-init-config.erb @@ -0,0 +1,77 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: <%= @exec %> +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Default-Stop: 0 1 6 +# Description: <%= @description %> +### END INIT INFO + +# chkconfig: - 98 02 + + +. /etc/rc.d/init.d/functions + +name="<%= @exec =%>" + +[ -e "/etc/sysconfig/openstack-swift-$name" ] && . "/etc/sysconfig/openstack-swift-$name" + +lockfile="/var/lock/subsys/openstack-swift-$name" + +start() { + swift-init "$name" start + retval=$? + [ $retval -eq 0 ] && touch $lockfile + return $retval +} + +stop() { + swift-init "$name" stop + retval=$? + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +rh_status() { + swift-init "$name" status + retval=$? + return $retval +} + +rh_status_q() { + rh_status &> /dev/null +} + + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart}" + exit 2 +esac +exit $? diff --git a/chef/cookbooks/openstack-object-storage/templates/default/simple-systemd-config.erb b/chef/cookbooks/openstack-object-storage/templates/default/simple-systemd-config.erb new file mode 100644 index 0000000..3958250 --- /dev/null +++ b/chef/cookbooks/openstack-object-storage/templates/default/simple-systemd-config.erb @@ -0,0 +1,11 @@ +[Unit] +Description=<%= @description %> +After=syslog.target network.target + +[Service] +Type=simple +User=<%= @user %> +ExecStart=<%= @exec %> + +[Install] +WantedBy=multi-user.target diff --git a/chef/cookbooks/openstack-ops-database/CHANGELOG.md b/chef/cookbooks/openstack-ops-database/CHANGELOG.md new file mode 100644 index 0000000..d485638 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/CHANGELOG.md @@ -0,0 +1,3 @@ +## 2013.1.0 + +* initial release diff --git a/chef/cookbooks/openstack-ops-database/LICENSE b/chef/cookbooks/openstack-ops-database/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-ops-database/README.md b/chef/cookbooks/openstack-ops-database/README.md new file mode 100644 index 0000000..920fce3 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/README.md @@ -0,0 +1,83 @@ +# Description # + +This cookbook provides shared database configuration for the OpenStack **Grizzly** reference deployment provided by Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. It currently supports MySQL and will soon support PostgreSQL. + +# Requirements # + +Chef 11 with Ruby 1.9.x required. + +# Platforms # + +* Ubuntu-12.04 + +# Cookbooks # + +The following cookbooks are dependencies: + +* database +* mysql +* openssl + +# Resources/Providers # + +None + +# Templates # + +None + +# Recipes # + +## client ## + +- database client configuration, selected by attributes + +## server ## + +- database server configuration, selected by attributes + +## mysql-client ## + +- calls mysql::ruby and mysql::client and installs 'mysql_python_packages' + +## mysql-server ## + +- configures the mysql server for OpenStack + +# Attributes # + +* `openstack['role']['database]` - which role should other nodes search on to find the database service, defaults to 'os-ops-database' + +* `openstack['database']['service']` - which service to use, defaults to 'mysql' +* `openstack['database']['platform']['mysql_python_packages']` - platform-specific mysql python packages to install + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | Justin Shepherd () | +| **Author** | Jason Cannavale () | +| **Author** | Ron Pedde () | +| **Author** | Joseph Breu () | +| **Author** | William Kelly () | +| **Author** | Darren Birkett () | +| **Author** | Evan Callicoat () | +| **Author** | Matt Thompson () | +| **Author** | Matt Ray () | +| | | +| **Copyright** | Copyright (c) 2012-2013, Rackspace US, Inc. | +| **Copyright** | Copyright (c) 2012-2013, Opscode, Inc. | + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-ops-database/attributes/default.rb b/chef/cookbooks/openstack-ops-database/attributes/default.rb new file mode 100644 index 0000000..80efa58 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/attributes/default.rb @@ -0,0 +1,11 @@ +default['openstack']['role']['database'] = 'os-ops-database' + +default['openstack']['database']['service'] = 'mysql' + +# platform defaults +case platform +when 'fedora', 'redhat', 'centos' # :pragma-foodcritic: ~FC024 - won't fix this + default['openstack']['database']['platform']['mysql_python_packages'] = [ 'MySQL-python' ] +when 'ubuntu' + default['openstack']['database']['platform']['mysql_python_packages'] = [ 'python-mysqldb' ] +end diff --git a/chef/cookbooks/openstack-ops-database/metadata.rb b/chef/cookbooks/openstack-ops-database/metadata.rb new file mode 100644 index 0000000..2e54594 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/metadata.rb @@ -0,0 +1,16 @@ +name "openstack-ops-database" +maintainer "Opscode, Inc." +maintainer_email "matt@opscode.com" +license "Apache 2.0" +description "Provides the shared database configuration for Chef for OpenStack." +version "0.1.0" + +recipe "default", "Selects the database service." +recipe "mysql", "Configures MySQL." + +%w{ ubuntu }.each do |os| + supports os +end + +depends "database", ">= 1.3.12" +depends "mysql", ">= 3.0.0" diff --git a/chef/cookbooks/openstack-ops-database/recipes/client.rb b/chef/cookbooks/openstack-ops-database/recipes/client.rb new file mode 100644 index 0000000..63e934f --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/client.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: client +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-ops-database::#{node['openstack']['database']['service']}-client" diff --git a/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb b/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb new file mode 100644 index 0000000..b7def7f --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/mysql-client.rb @@ -0,0 +1,23 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: mysql-client +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "mysql::ruby" +include_recipe "mysql::client" + +node.default['openstack']['packages']['ops-database::mysql-client'] = node['openstack']['database']['platform']['mysql_python_packages'] diff --git a/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb b/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb new file mode 100644 index 0000000..3888977 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/mysql-server.rb @@ -0,0 +1,69 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: mysql-server +# +# Copyright 2013, Opscode, Inc. +# Copyright 2012-2013, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# override default attributes in the upstream mysql cookbook +node.set['mysql']['bind_address'] = node['openstack']['db']['bind_address'] +node.set['mysql']['server_debian_password'] = node['openstack']['db']['super']['password'] +node.set['mysql']['server_root_password'] = node['openstack']['db']['super']['password'] +node.set['mysql']['server_repl_password'] = node['openstack']['db']['super']['password'] + +node.set['mysql']['tunable']['innodb_thread_concurrency'] = "0" +node.set['mysql']['tunable']['innodb_commit_concurrency'] = "0" +node.set['mysql']['tunable']['innodb_read_io_threads'] = "4" +node.set['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = "2" + +node.save + +include_recipe "openstack-ops-database::mysql-client" +include_recipe "mysql::server" + +mysql_connection_info = {:host => "localhost", + :username => 'root', + :password => node['mysql']['server_root_password']} + +# removing insecure default mysql users +mysql_database_user 'drop empty localhost user' do + username '' + host 'localhost' + connection mysql_connection_info + action :drop +end + +# removing insecure default mysql users +mysql_database_user 'drop empty hostname user' do + username '' + host "#{node.hostname}" + connection mysql_connection_info + action :drop +end + +# drop the test database +mysql_database 'test' do + connection mysql_connection_info + action :drop +end + +# flush the privileges +mysql_database "FLUSH privileges" do + connection mysql_connection_info + sql "FLUSH privileges" + action :nothing + subscribes :query, "mysql_database[test]" +end diff --git a/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb b/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb new file mode 100644 index 0000000..8b8a072 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/openstack-db.rb @@ -0,0 +1,65 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: openstack-db +# +# Copyright 2012-2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +db_create_with_user( + "compute", + node["openstack"]["db"]["compute"]["username"], + db_password(node['openstack']['db']['compute']['password']) +) + +db_create_with_user( + "dashboard", + node["openstack"]["db"]["dashboard"]["username"], + db_password(node['openstack']['db']['dashboard']['password']) +) + +db_create_with_user( + "identity", + node["openstack"]["db"]["identity"]["username"], + db_password(node['openstack']['db']['identity']['password']) +) + +db_create_with_user( + "image", + node["openstack"]["db"]["image"]["username"], + db_password(node['openstack']['db']['image']['password']) +) + +db_create_with_user( + "metering", + node["openstack"]["db"]["metering"]["username"], + db_password(node['openstack']['db']['metering']['password']) +) + +db_create_with_user( + "network", + node["openstack"]["db"]["network"]["username"], + db_password(node['openstack']['db']['network']['password']) +) + +db_create_with_user( + "volume", + node["openstack"]["db"]["volume"]["username"], + db_password(node['openstack']['db']['volume']['password']) +) + diff --git a/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb b/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb new file mode 100644 index 0000000..5d259b4 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/postgresql-client.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: postgresql-client +# +# Copyright 2013, Opscode, Inc. +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::ruby" +include_recipe "postgresql::client" + +node["openstack"]["db"]["platform"]["postgresql_python_packages"].each do |pkg| + package pkg +end diff --git a/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb b/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb new file mode 100644 index 0000000..56bf4e9 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/postgresql-server.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: postgresql-server +# +# Copyright 2013, Opscode, Inc. +# Copyright 2012-2013, Rackspace US, Inc. +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, SUSE Linux GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +listen_address = address_for node["openstack"]["db"]["bind_interface"] + +node.override["postgresql"]["config"]["listen_addresses"] = listen_address + +include_recipe "openstack-ops-database::postgresql-client" +include_recipe "postgresql::server" diff --git a/chef/cookbooks/openstack-ops-database/recipes/server.rb b/chef/cookbooks/openstack-ops-database/recipes/server.rb new file mode 100644 index 0000000..09f4b40 --- /dev/null +++ b/chef/cookbooks/openstack-ops-database/recipes/server.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-ops-database +# Recipe:: server +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-ops-database::#{node['openstack']['database']['service']}-server" diff --git a/chef/cookbooks/openstack-ops-messaging/.tailor b/chef/cookbooks/openstack-ops-messaging/.tailor new file mode 100644 index 0000000..99f0dcf --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/.tailor @@ -0,0 +1,25 @@ +Tailor.config do |config| + config.formatters "text" + config.file_set '**/*.rb' do |style| + style.max_line_length 80, level: :off + style.allow_camel_case_methods false, level: :error + style.allow_hard_tabs false, level: :error + style.allow_screaming_snake_case_classes false, level: :error + style.allow_trailing_line_spaces false, level: :error + style.allow_invalid_ruby false, level: :warn + style.indentation_spaces 2, level: :error + style.max_code_lines_in_class 300, level: :error + style.max_code_lines_in_method 30, level: :error + style.spaces_after_comma 1, level: :error + style.spaces_after_lbrace 1, level: :error + style.spaces_after_lbracket 0, level: :error + style.spaces_after_lparen 0, level: :error + style.spaces_before_comma 0, level: :error + style.spaces_before_lbrace 1, level: :error + style.spaces_before_rbrace 1, level: :error + style.spaces_before_rbracket 0, level: :error + style.spaces_before_rparen 0, level: :error + style.spaces_in_empty_braces 0, level: :error + style.trailing_newlines 1, level: :error + end +end diff --git a/chef/cookbooks/openstack-ops-messaging/Berksfile b/chef/cookbooks/openstack-ops-messaging/Berksfile new file mode 100644 index 0000000..84e5b6d --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/Berksfile @@ -0,0 +1,4 @@ +metadata + +cookbook "openstack-common", + git: "git://github.com/stackforge/cookbook-openstack-common.git" diff --git a/chef/cookbooks/openstack-ops-messaging/Berksfile.lock b/chef/cookbooks/openstack-ops-messaging/Berksfile.lock new file mode 100644 index 0000000..517fc20 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/Berksfile.lock @@ -0,0 +1,46 @@ +{ + "sha": "45e1bf81e0dd550088ee5794e167786e616570ea", + "sources": { + "openstack-ops-messaging": { + "path": "." + }, + "openstack-common": { + "locked_version": "0.3.0", + "git": "git://github.com/stackforge/cookbook-openstack-common.git", + "ref": "25b183f2362fa501cfee4db331491b3d984a5c05" + }, + "rabbitmq": { + "locked_version": "2.1.2" + }, + "erlang": { + "locked_version": "1.3.0" + }, + "apt": { + "locked_version": "2.0.0" + }, + "yum": { + "locked_version": "2.3.0" + }, + "build-essential": { + "locked_version": "1.4.0" + }, + "database": { + "locked_version": "1.4.0" + }, + "mysql": { + "locked_version": "3.0.2" + }, + "openssl": { + "locked_version": "1.0.2" + }, + "postgresql": { + "locked_version": "3.0.2" + }, + "aws": { + "locked_version": "0.101.2" + }, + "xfs": { + "locked_version": "1.1.0" + } + } +} diff --git a/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md b/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md new file mode 100644 index 0000000..fa14ddd --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/CHANGELOG.md @@ -0,0 +1,4 @@ +## 7.0.0 + +* Initial release intended for Grizzly-based OpenStack releases, + for use with Stackforge upstream repositories. diff --git a/chef/cookbooks/openstack-ops-messaging/Gemfile b/chef/cookbooks/openstack-ops-messaging/Gemfile new file mode 100644 index 0000000..04ef97e --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem "chef", "~> 11.4.4" +gem "json", "<= 1.7.7" # chef 11 dependency +gem "berkshelf", "~> 2.0.3" +gem "chefspec", "~> 1.3.0" +gem "foodcritic" +gem "strainer" +gem "tailor" diff --git a/chef/cookbooks/openstack-ops-messaging/Gemfile.lock b/chef/cookbooks/openstack-ops-messaging/Gemfile.lock new file mode 100644 index 0000000..2d00f7d --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/Gemfile.lock @@ -0,0 +1,223 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.0.0) + i18n (~> 0.6, >= 0.6.4) + minitest (~> 4.2) + multi_json (~> 1.3) + thread_safe (~> 0.1) + tzinfo (~> 0.3.37) + addressable (2.3.5) + akami (1.2.0) + gyoku (>= 0.4.0) + nokogiri (>= 1.4.0) + atomic (1.1.10) + berkshelf (2.0.5) + activesupport (>= 3.2.0) + addressable (~> 2.3.4) + buff-shell_out (~> 0.1) + celluloid (>= 0.14.0) + chozo (>= 0.6.1) + faraday (>= 0.8.5) + hashie (>= 2.0.2) + minitar (~> 0.5.4) + rbzip2 (~> 0.2.0) + retryable (~> 1.3.3) + ridley (~> 1.2.1) + solve (>= 0.5.0) + thor (~> 0.18.0) + buff-extensions (0.5.0) + buff-ruby_engine (0.1.0) + buff-shell_out (0.1.0) + buff-ruby_engine (~> 0.1.0) + builder (3.2.2) + celluloid (0.14.1) + timers (>= 1.0.0) + celluloid-io (0.14.1) + celluloid (>= 0.14.1) + nio4r (>= 0.4.5) + chef (11.4.4) + erubis + highline (>= 1.6.9) + json (>= 1.4.4, <= 1.7.7) + mixlib-authentication (>= 1.3.0) + mixlib-cli (~> 1.3.0) + mixlib-config (>= 1.1.2) + mixlib-log (>= 1.3.0) + mixlib-shellout + net-ssh (~> 2.6) + net-ssh-multi (~> 1.1.0) + ohai (>= 0.6.0) + rest-client (>= 1.0.4, < 1.7.0) + yajl-ruby (~> 1.1) + chefspec (1.3.1) + chef (>= 10.0) + erubis + fauxhai (>= 0.1.1, < 2.0) + minitest-chef-handler (>= 0.6.0) + rspec (~> 2.0) + chozo (0.6.1) + activesupport (>= 3.2.0) + hashie (>= 2.0.2) + multi_json (>= 1.3.0) + ci_reporter (1.8.4) + builder (>= 2.1.2) + diff-lcs (1.2.4) + erubis (2.7.0) + faraday (0.8.7) + multipart-post (~> 1.1) + fauxhai (1.1.1) + httparty + net-ssh + ohai + ffi (1.9.0) + foodcritic (2.1.0) + erubis + gherkin (~> 2.11.7) + nokogiri (~> 1.5.4) + rak (~> 1.4) + treetop (~> 1.4.10) + yajl-ruby (~> 1.1.0) + gherkin (2.11.8) + multi_json (~> 1.3) + gssapi (1.0.3) + ffi (>= 1.0.1) + gyoku (1.0.0) + builder (>= 2.1.2) + hashie (2.0.5) + highline (1.6.19) + httparty (0.11.0) + multi_json (~> 1.0) + multi_xml (>= 0.5.2) + httpclient (2.2.0.2) + httpi (0.9.7) + rack + i18n (0.6.4) + ipaddress (0.8.0) + json (1.7.7) + little-plugger (1.1.3) + log_switch (0.4.0) + logging (1.6.2) + little-plugger (>= 1.1.3) + mime-types (1.23) + minitar (0.5.4) + minitest (4.7.5) + minitest-chef-handler (1.0.1) + chef + ci_reporter + minitest (~> 4.7.3) + mixlib-authentication (1.3.0) + mixlib-log + mixlib-cli (1.3.0) + mixlib-config (1.1.2) + mixlib-log (1.6.0) + mixlib-shellout (1.1.0) + multi_json (1.7.7) + multi_xml (0.5.4) + multipart-post (1.2.0) + net-http-persistent (2.8) + net-ssh (2.6.7) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + net-ssh-multi (1.1) + net-ssh (>= 2.1.4) + net-ssh-gateway (>= 0.99.0) + nio4r (0.4.6) + nokogiri (1.5.10) + nori (1.1.5) + ohai (6.16.0) + ipaddress + mixlib-cli + mixlib-config + mixlib-log + mixlib-shellout + systemu + yajl-ruby + polyglot (0.3.3) + rack (1.5.2) + rak (1.4) + rbzip2 (0.2.0) + rest-client (1.6.7) + mime-types (>= 1.16) + retryable (1.3.3) + ridley (1.2.3) + addressable + buff-extensions (~> 0.3) + buff-shell_out (~> 0.1) + celluloid (~> 0.14.0) + celluloid-io (~> 0.14.0) + erubis + faraday (>= 0.8.4) + hashie (>= 2.0.2) + json (>= 1.7.7) + mixlib-authentication (>= 1.3.0) + net-http-persistent (>= 2.8) + net-ssh + retryable + solve (>= 0.4.4) + varia_model (~> 0.1) + winrm (~> 1.1.0) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rubyntlm (0.1.1) + savon (0.9.5) + akami (~> 1.0) + builder (>= 2.1.2) + gyoku (>= 0.4.0) + httpi (~> 0.9) + nokogiri (>= 1.4.0) + nori (~> 1.0) + wasabi (~> 1.0) + solve (0.6.0) + strainer (3.0.3) + berkshelf (~> 2.0) + systemu (2.5.2) + tailor (1.2.1) + log_switch (>= 0.3.0) + term-ansicolor (>= 1.0.5) + text-table (>= 1.2.2) + term-ansicolor (1.2.2) + tins (~> 0.8) + text-table (1.2.3) + thor (0.18.1) + thread_safe (0.1.0) + atomic + timers (1.1.0) + tins (0.8.2) + treetop (1.4.14) + polyglot + polyglot (>= 0.3.1) + tzinfo (0.3.37) + uuidtools (2.1.4) + varia_model (0.1.0) + buff-extensions (~> 0.1) + hashie (>= 2.0.2) + wasabi (1.0.0) + nokogiri (>= 1.4.0) + winrm (1.1.2) + gssapi (~> 1.0.0) + httpclient (~> 2.2.0.2) + logging (~> 1.6.1) + nokogiri (~> 1.5.0) + rubyntlm (~> 0.1.1) + savon (= 0.9.5) + uuidtools (~> 2.1.2) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + berkshelf (~> 2.0.3) + chef (~> 11.4.4) + chefspec (~> 1.3.0) + foodcritic + json (<= 1.7.7) + strainer + tailor diff --git a/chef/cookbooks/openstack-ops-messaging/LICENSE b/chef/cookbooks/openstack-ops-messaging/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-ops-messaging/README.md b/chef/cookbooks/openstack-ops-messaging/README.md new file mode 100644 index 0000000..347470f --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/README.md @@ -0,0 +1,84 @@ +# Description # + +This cookbook provides shared message queue configuration for the OpenStack **Grizzly** reference deployment provided by Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. It currently supports RabbitMQ and will soon other queues. + +# Requirements # + +Chef 11 with Ruby 1.9.x required. + +# Platforms # + +* Ubuntu-12.04 + +# Cookbooks # + +The following cookbooks are dependencies: + +* openstack-common +* rabbitmq + +# Usage # + +The usage of this cookbook is optional, you may choose to set up your own messaging service without using this cookbook. If you choose to do so, you will need to provide all of the attributes listed under the [Attributes](#attributes). + +# Resources/Providers # + +None + +# Templates # + +None + +# Recipes # + +## server ## + +- message queue server configuration, selected by attributes + +## rabbitmq-server ## + +- configures the RabbitMQ server for OpenStack + +# Attributes # + +* `openstack["mq"]["bind_interface"]` - bind to interfaces IPv4 address +* `openstack["mq"]["cluster"]` - whether or not to cluster rabbit, defaults to 'false' + +Testing +===== + +This cookbook uses [bundler](http://gembundler.com/), [berkshelf](http://berkshelf.com/), and [strainer](https://github.com/customink/strainer) to isolate dependencies and run tests. + +Tests are defined in Strainerfile. + +To run tests: + + $ bundle install # install gem dependencies + $ bundle exec berks install # install cookbook dependencies + $ bundle exec strainer test # run tests + +License and Author +================== + +| | | +|:---------------------|:---------------------------------------------------| +| **Author** | John Dewey () | +| **Author** | Matt Ray () | +| **Author** | Craig Tracey () | +| | | +| **Copyright** | Copyright (c) 2013, Opscode, Inc. | +| **Copyright** | Copyright (c) 2013, Craig Tracey | +| **Copyright** | Copyright (c) 2013, AT&T Services, Inc. | + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/openstack-ops-messaging/Strainerfile b/chef/cookbooks/openstack-ops-messaging/Strainerfile new file mode 100644 index 0000000..7e292b4 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/Strainerfile @@ -0,0 +1,5 @@ +# Strainerfile +tailor: bundle exec tailor +knife test: bundle exec knife cookbook test $COOKBOOK +foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK +chefspec: bundle exec rspec $SANDBOX/$COOKBOOK/spec diff --git a/chef/cookbooks/openstack-ops-messaging/attributes/default.rb b/chef/cookbooks/openstack-ops-messaging/attributes/default.rb new file mode 100644 index 0000000..95782cc --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/attributes/default.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: openstack-ops-messaging +# Recipe:: default +# +# Copyright 2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default["openstack"]["mq"]["bind_interface"] = "lo" +default["openstack"]["mq"]["cluster"] = false diff --git a/chef/cookbooks/openstack-ops-messaging/metadata.rb b/chef/cookbooks/openstack-ops-messaging/metadata.rb new file mode 100644 index 0000000..093386f --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/metadata.rb @@ -0,0 +1,16 @@ +name "openstack-ops-messaging" +maintainer "Opscode, Inc." +maintainer_email "matt@opscode.com" +license "Apache 2.0" +description "Provides the shared messaging configuration for Chef for OpenStack." +version "7.0.0" + +recipe "server", "Installs and configures server packages for messaging queue used by the deployment." +recipe "rabbitmq-server", "Installs and configures RabbitMQ and is called via the server recipe" + +%w{ fedora ubuntu redhat centos }.each do |os| + supports os +end + +depends "openstack-common", "~> 0.4.0" +depends "rabbitmq", ">= 2.1.0" diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/default.rb b/chef/cookbooks/openstack-ops-messaging/recipes/default.rb new file mode 100644 index 0000000..10fc737 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: openstack-ops-messaging +# Recipe:: default +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-ops-messaging::#{node['openstack']['messaging']['service']}" diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb new file mode 100644 index 0000000..3dab40a --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq-server.rb @@ -0,0 +1,90 @@ +# +# Cookbook Name:: openstack-ops-messaging +# Recipe:: rabbitmq-server +# +# Copyright 2013, Opscode, Inc. +# Copyright 2013, AT&T Services, Inc. +# Copyright 2013, Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +rabbit_server_role = node["openstack"]["mq"]["server_role"] +user = node["openstack"]["mq"]["user"] +password = node["openstack"]["mq"]["password"] +pass = user_password password +vhost = node["openstack"]["mq"]["vhost"] +bind_interface = node["openstack"]["mq"]["bind_interface"] +#listen_address = address_for node["openstack"]["mq"]["bind_interface"] unless defined?(node['openstack']['mq']['bind_address']) +listen_address = node['openstack']['mq']['bind_address'] + +# Used by OpenStack#rabbit_servers/#rabbit_server +node.set["openstack"]["mq"]["listen"] = listen_address + +node.override["rabbitmq"]["port"] = node["openstack"]["mq"]["port"] +node.override["rabbitmq"]["address"] = listen_address +node.override["rabbitmq"]["default_user"] = user +node.override["rabbitmq"]["default_pass"] = pass +node.override["rabbitmq"]["use_distro_version"] = true + +# Clustering +if node["openstack"]["mq"]["cluster"] + node.override["rabbitmq"]["cluster"] = node["openstack"]["mq"]["cluster"] + node.override["rabbitmq"]["erlang_cookie"] = service_password "rabbit_cookie" + qs = "roles:#{rabbit_server_role} AND chef_environment:#{node.chef_environment}" + node.override["rabbitmq"]["cluster_disk_nodes"] = search(:node, qs).map do |n| + "#{user}@#{n['hostname']}" + end.sort +end + +include_recipe "rabbitmq" +include_recipe "rabbitmq::mgmt_console" + +rabbitmq_user "remove rabbit guest user" do + user "guest" + action :delete + + not_if { user == "guest" } +end + +rabbitmq_user "add openstack rabbit user" do + user user + password pass + + action :add +end + +rabbitmq_vhost "add openstack rabbit vhost" do + vhost vhost + + action :add +end + +rabbitmq_user "set openstack user permissions" do + user user + vhost vhost + permissions '.* .* .*' + action :set_permissions +end + +# Necessary for graphing. +rabbitmq_user "set rabbit administrator tag" do + user user + tag "administrator" + + action :set_tags +end diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb new file mode 100644 index 0000000..e867748 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/recipes/rabbitmq.rb @@ -0,0 +1,58 @@ +# +# Cookbook Name:: openstack-ops-messaging +# Recipe:: rabbitmq +# +# Copyright 2013, Opscode, Inc. +# Copyright 2012, John Dewey +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) + +node.set_unless['openstack']['messaging']['password'] = secure_password +node.set_unless['rabbitmq']['address'] = '0.0.0.0' +node.set_unless['rabbitmq']['port'] = 5672 + +include_recipe "rabbitmq" +include_recipe "rabbitmq::mgmt_console" + +user = node['openstack']['messaging']['user'] +vhost = node['openstack']['messaging']['vhost'] + +# remove the guest user +rabbitmq_user 'guest' do + action :delete + not_if { user.eql?('guest') } +end + +rabbitmq_user user do + password node['openstack']['messaging']['password'] + action :add +end + +rabbitmq_vhost vhost do + action :add +end + +rabbitmq_user user do + vhost vhost + permissions ".* .* .*" + action :set_permissions +end + +# Necessary for graphing. +rabbitmq_user user do + tag "administrator" + action :set_tags +end diff --git a/chef/cookbooks/openstack-ops-messaging/recipes/server.rb b/chef/cookbooks/openstack-ops-messaging/recipes/server.rb new file mode 100644 index 0000000..35ccd73 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/recipes/server.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: openstack-ops-messaging +# Recipe:: server +# +# Copyright 2013, Opscode, Inc. +# Copyright 2013, Craig Tracey +# Copyright 2013, AT&T Services, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openstack-ops-messaging::#{node["openstack"]["mq"]["service_type"]}-server" diff --git a/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb new file mode 100644 index 0000000..817387d --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/spec/rabbitmq-server_spec.rb @@ -0,0 +1,130 @@ +require_relative "spec_helper" + +describe "openstack-ops-messaging::rabbitmq-server" do + before { ops_messaging_stubs } + describe "ubuntu" do + before do + @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + @chef_run.converge "openstack-ops-messaging::rabbitmq-server" + end + + it "overrides default rabbit attributes" do + expect(@chef_run.node["openstack"]["mq"]["port"]).to eql "5672" + expect(@chef_run.node["openstack"]["mq"]["listen"]).to eql "127.0.0.1" + expect(@chef_run.node["rabbitmq"]["address"]).to eql "127.0.0.1" + expect(@chef_run.node["rabbitmq"]["default_user"]).to eql "guest" + expect(@chef_run.node['rabbitmq']['default_pass']).to eql "rabbit-pass" + end + + describe "cluster" do + before do + @chef_run = ::ChefSpec::ChefRunner.new(::UBUNTU_OPTS) do |n| + n.set["openstack"]["mq"] = { + "cluster" => true + } + end + @chef_run.converge "openstack-ops-messaging::rabbitmq-server" + end + + it "overrides cluster" do + expect(@chef_run.node['rabbitmq']['cluster']).to be_true + end + + it "overrides erlang_cookie" do + expect(@chef_run.node['rabbitmq']['erlang_cookie']).to eql( + "erlang-cookie" + ) + end + + it "overrides and sorts cluster_disk_nodes" do + expect(@chef_run.node['rabbitmq']['cluster_disk_nodes']).to eql( + ["guest@host1", "guest@host2"] + ) + end + end + + it "includes rabbit recipes" do + expect(@chef_run).to include_recipe "rabbitmq" + expect(@chef_run).to include_recipe "rabbitmq::mgmt_console" + end + + describe "lwrps" do + it "deletes guest user" do + resource = @chef_run.find_resource( + "rabbitmq_user", + "remove rabbit guest user" + ).to_hash + + expect(resource).to include( + :user => "guest", + :action => [:delete] + ) + end + + it "doesn't delete guest user" do + opts = ::UBUNTU_OPTS.merge(:evaluate_guards => true) + chef_run = ::ChefSpec::ChefRunner.new opts + chef_run.converge "openstack-ops-messaging::rabbitmq-server" + + resource = chef_run.find_resource( + "rabbitmq_user", + "remove rabbit guest user" + ) + + expect(resource).to be_nil + end + + it "adds user" do + resource = @chef_run.find_resource( + "rabbitmq_user", + "add openstack rabbit user" + ).to_hash + + expect(resource).to include( + :user => "guest", + :password => "rabbit-pass", + :action => [:add] + ) + end + + it "adds vhost" do + resource = @chef_run.find_resource( + "rabbitmq_vhost", + "add openstack rabbit vhost" + ).to_hash + + expect(resource).to include( + :vhost => "/", + :action => [:add] + ) + end + + it "sets user permissions" do + resource = @chef_run.find_resource( + "rabbitmq_user", + "set openstack user permissions" + ).to_hash + + expect(resource).to include( + :user => "guest", + :vhost => "/", + :permissions => '.* .* .*', + :action => [:set_permissions] + ) + end + + it "sets administrator tag" do + resource = @chef_run.find_resource( + "rabbitmq_user", + "set rabbit administrator tag" + ).to_hash + + expect(resource).to include( + :user => "guest", + :tag => "administrator", + :action => [:set_tags] + ) + end + end + end +end diff --git a/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb b/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb new file mode 100644 index 0000000..139a7f4 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/spec/server_spec.rb @@ -0,0 +1,14 @@ +require_relative 'spec_helper' + +describe "openstack-ops-messaging::server" do + before { ops_messaging_stubs } + describe "ubuntu" do + + it "uses proper messaging server recipe" do + chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS + chef_run.converge "openstack-ops-messaging::server" + + expect(chef_run).to include_recipe "openstack-ops-messaging::rabbitmq-server" + end + end +end diff --git a/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb b/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb new file mode 100644 index 0000000..5a94c19 --- /dev/null +++ b/chef/cookbooks/openstack-ops-messaging/spec/spec_helper.rb @@ -0,0 +1,30 @@ +require "chefspec" + +::LOG_LEVEL = :fatal +::REDHAT_OPTS = { + :platform => "redhat", + :version => "6.3", + :log_level => ::LOG_LEVEL +} +::UBUNTU_OPTS = { + :platform => "ubuntu", + :version => "12.04", + :log_level => ::LOG_LEVEL +} + +def ops_messaging_stubs + ::Chef::Recipe.any_instance.stub(:address_for). + with("lo"). + and_return "127.0.0.1" + ::Chef::Recipe.any_instance.stub(:search). + with(:node, "roles:os-ops-messaging AND chef_environment:_default"). + and_return [ + { 'hostname' => 'host2' }, + { 'hostname' => 'host1' } + ] + ::Chef::Recipe.any_instance.stub(:user_password). + and_return "rabbit-pass" + ::Chef::Recipe.any_instance.stub(:service_password). + with("rabbit_cookie"). + and_return "erlang-cookie" +end diff --git a/chef/cookbooks/postgresql/.kitchen.yml b/chef/cookbooks/postgresql/.kitchen.yml new file mode 100644 index 0000000..0aa1305 --- /dev/null +++ b/chef/cookbooks/postgresql/.kitchen.yml @@ -0,0 +1,144 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: + - recipe[minitest-handler] + - recipe[postgresql] + attributes: {} + +- name: contrib + run_list: + - recipe[postgresql::contrib] + attributes: + postgresql: + password: + postgres: "iloverandompasswordsbutthiswilldo" + +- name: apt-pgdg-client + run_list: + - recipe[minitest-handler] + - recipe[postgresql] + excludes: ["centos-5.9", "centos-6.4"] + attributes: + postgresql: + enable_pgdg_apt: true + version: "9.2" + client: + packages: ["postgresql-client-9.2", "libpq-dev"] + +- name: yum-pgdg-client + run_list: + - recipe[minitest-handler] + - recipe[postgresql] + excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] + attributes: + postgresql: + enable_pgdg_yum: true + version: "9.2" + client: + packages: ["postgresql92"] + +- name: ruby + run_list: + - recipe[postgresql::ruby] + - recipe[minitest-handler] + attributes: {} + +- name: server + run_list: + - recipe[postgresql::ruby] + - recipe[minitest-handler] + - recipe[postgresql::server] + attributes: + postgresql: + password: + postgres: "iloverandompasswordsbutthiswilldo" + +- name: apt-pgdg-server + run_list: + - recipe[minitest-handler] + - recipe[postgresql::ruby] + - recipe[postgresql::server] + excludes: ["centos-5.9", "centos-6.4"] + attributes: + postgresql: + enable_pgdg_apt: true + version: "9.2" + server: + packages: ["postgresql-9.2"] + password: + postgres: "iloverandompasswordsbutthiswilldo" + config: + ssl_cert_file: "/etc/ssl/certs/ssl-cert-snakeoil.pem" + ssl_key_file: "/etc/ssl/private/ssl-cert-snakeoil.key" + +- name: yum-pgdg-server + run_list: + - recipe[minitest-handler] + - recipe[postgresql::ruby] + - recipe[postgresql::server] + excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] + attributes: + postgresql: + enable_pgdg_yum: true + version: "9.2" + server: + packages: ["postgresql92-server"] + service_name: "postgresql-9.2" + password: + postgres: "iloverandompasswordsbutthiswilldo" + +- name: apt-pgdg-client-ruby + run_list: + - recipe[minitest-handler] + - recipe[postgresql] + - recipe[postgresql::ruby] + excludes: ["centos-5.9", "centos-6.4"] + attributes: + postgresql: + enable_pgdg_apt: true + version: "9.2" + client: + packages: ["postgresql-client-9.2", "libpq-dev"] + +- name: yum-pgdg-client-ruby + run_list: + - recipe[minitest-handler] + - recipe[postgresql] + - recipe[postgresql::ruby] + excludes: ["ubuntu-10.04", "ubuntu-12.04", "debian-6.0.7"] + attributes: + postgresql: + enable_pgdg_yum: true + version: "9.2" + client: + packages: ["postgresql92", "postgresql92-devel"] diff --git a/chef/cookbooks/postgresql/Berksfile b/chef/cookbooks/postgresql/Berksfile new file mode 100644 index 0000000..52dc4f4 --- /dev/null +++ b/chef/cookbooks/postgresql/Berksfile @@ -0,0 +1,7 @@ +site :opscode + +metadata + +group :integration do + cookbook 'minitest-handler' +end diff --git a/chef/cookbooks/postgresql/CHANGELOG.md b/chef/cookbooks/postgresql/CHANGELOG.md new file mode 100644 index 0000000..cdbb542 --- /dev/null +++ b/chef/cookbooks/postgresql/CHANGELOG.md @@ -0,0 +1,103 @@ +postgresql Cookbook CHANGELOG +============================= +This file is used to list changes made in each version of the postgresql cookbook. + + +v3.0.4 +------ +### Bug +- **[COOK-3173](https://tickets.opscode.com/browse/COOK-3173)** - Use :reload instead of :restart on conf changes +- **[COOK-2939](https://tickets.opscode.com/browse/COOK-2939)** - Fix RedHat support + +v3.0.2 +------ +### Bug +- [COOK-3076]: postgresql::ruby recipe error when using pgdg repositories + +v3.0.0 +------ +This is a backwards-incompatible release because the Pitti PPA is deprecated and the recipe removed, replaced with the PGDG apt repository. + +### Bug +- [COOK-2571]: Create helper library for pg extension detection +- [COOK-2797]: Contrib extension contianing '-' fails to load. + +### Improvement +- [COOK-2387]: Pitti Postgresql PPA is deprecated + +### Task +- [COOK-3022]: update baseboxes in .kitchen.yml + +v2.4.0 +------ +- [COOK-2163] - Dangerous "assign-postgres-password" in "recipes/server.rb" -- Can lock out dbadmin access +- [COOK-2390] - Recipes to auto-generate many postgresql.conf settings, following "initdb" and "pgtune" +- [COOK-2435] - Foodcritic fixes for postgresql cookbook +- [COOK-2476] - Installation into database of any contrib module extensions listed in a node attribute + +v2.2.2 +------ +- [COOK-2232] -Provide PGDG yum repo to install postgresql 9.x on + redhat-derived distributions + +v2.2.0 +------ +- [COOK-2230] - Careful about Debian minor version numbers +- [COOK-2231] - Fix support for postgresql 9.x in server_redhat recipe +- [COOK-2238] - Postgresql recipe error in password check +- [COOK-2176] - PostgreSQL cookbook in Solo mode can cause "NoMethodError: undefined method `[]' for nil:NilClass" +- [COOK-2233] - Provide postgresql::contrib recipe to install useful server administration tools + +v2.1.0 +------ +- [COOK-1872] - Allow latest PostgreSQL deb packages to be installed +- [COOK-1961] - Postgresql config file changes with every Chef run +- [COOK-2041] - Postgres cookbook no longer installs on OpenSuSE 11.4 + +v2.0.2 +------ +- [COOK-1406] - pg gem compile is unable to find libpq under Chef full stack (omnibus) installation + +v2.0.0 +------ +This version is backwards incompatible with previous versions of the cookbook due to use of `platform_family`, and the refactored configuration files using node attributes. See README.md for details on how to modify configuration of PostgreSQL. + +- [COOK-1508] - fix mixlib shellout error on SUSE +- [COOK-1744] - Add service enable & start +- [COOK-1779] - Don't run apt-get update and others in ruby recipe if pg is installed +- [COOK-1871] - Attribute driven configuration files for PostgreSQL +- [COOK-1900] - don't assume ssl on all postgresql 8.4+ installs +- [COOK-1901] - fail a chef-solo run when the postgres password + attribute is not set + +v1.0.0 +------ +**Important note for this release** + +This version no longer installs Ruby bindings in the client recipe by default. Use the ruby recipe if you'd like the RubyGem. If you'd like packages for your distribution, use them in your application's specific cookbook/recipe, or modify the client packages attribute. + +This resolves the following tickets. + +- COOK-1011 +- COOK-1534 + +The following issues are also resolved with this release. + +- [COOK-1011] - Don't install postgresql packages during compile phase and remove pg gem installation +- [COOK-1224] - fix undefined variable on Debian +- [COOK-1462] - Add attribute for specifying listen address + +v0.99.4 +------ +- [COOK-421] - config template is malformed +- [COOK-956] - add make package on ubuntu/debian + +v0.99.2 +------ +- [COOK-916] - use < (with float) for version comparison. + +v0.99.0 +------ +- Better support for Red Hat-family platforms +- Integration with database cookbook +- Make sure the postgres role is updated with a (secure) password diff --git a/chef/cookbooks/postgresql/CONTRIBUTING.md b/chef/cookbooks/postgresql/CONTRIBUTING.md new file mode 100644 index 0000000..3a99897 --- /dev/null +++ b/chef/cookbooks/postgresql/CONTRIBUTING.md @@ -0,0 +1,257 @@ +# Contributing to Opscode Cookbooks + +We are glad you want to contribute to Opscode Cookbooks! The first +step is the desire to improve the project. + +You can find the answers to additional frequently asked questions +[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). + +You can find additional information about +[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) +on the wiki as well. + +## Quick-contribute + +* Create an account on our [bug tracker](http://tickets.opscode.com) +* Sign our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) +(keep reading if you're contributing on behalf of your employer) +* Create a ticket for your change on the + [bug tracker](http://tickets.opscode.com) +* Link to your patch as a rebased git branch or pull request from the + ticket +* Resolve the ticket as fixed + +We regularly review contributions and will get back to you if we have +any suggestions or concerns. + +## The Apache License and the CLA/CCLA + +Licensing is very important to open source projects, it helps ensure +the software continues to be available under the terms that the author +desired. Chef uses the Apache 2.0 license to strike a balance between +open contribution and allowing you to use the software however you +would like to. + +The license tells you what rights you have that are provided by the +copyright holder. It is important that the contributor fully +understands what rights they are licensing and agrees to them. +Sometimes the copyright holder isn't the contributor, most often when +the contributor is doing work for a company. + +To make a good faith effort to ensure these criteria are met, Opscode +requires a Contributor License Agreement (CLA) or a Corporate +Contributor License Agreement (CCLA) for all contributions. This is +without exception due to some matters not being related to copyright +and to avoid having to continually check with our lawyers about small +patches. + +It only takes a few minutes to complete a CLA, and you retain the +copyright to your contribution. + +You can complete our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). +If you're contributing on behalf of your employer, have your employer +fill out our +[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) +instead. + +## Ticket Tracker (JIRA) + +The [ticket tracker](http://tickets.opscode.com) is the most important +documentation for the code base. It provides significant historical +information, such as: + +* Which release a bug fix is included in +* Discussion regarding the design and merits of features +* Error output to aid in finding similar bugs + +Each ticket should aim to fix one bug or add one feature. + +## Using git + +You can get a quick copy of the repository for this cookbook by +running `git clone +git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. + +For collaboration purposes, it is best if you create a Github account +and fork the repository to your own account. Once you do this you will +be able to push your changes to your Github repository for others to +see and use. + +If you have another repository in your GitHub account named the same +as the cookbook, we suggest you suffix the repository with -cookbook. + +### Branches and Commits + +You should submit your patch as a git branch named after the ticket, +such as COOK-1337. This is called a _topic branch_ and allows users to +associate a branch of code with the ticket. + +It is a best practice to have your commit message have a _summary +line_ that includes the ticket number, followed by an empty line and +then a brief description of the commit. This also helps other +contributors understand the purpose of changes to the code. + + [COOK-1757] - platform_family and style + + * use platform_family for platform checking + * update notifies syntax to "resource_type[resource_name]" instead of + resources() lookup + * COOK-692 - delete config files dropped off by packages in conf.d + * dropped debian 4 support because all other platforms have the same + values, and it is older than "old stable" debian release + +Remember that not all users use Chef in the same way or on the same +operating systems as you, so it is helpful to be clear about your use +case and change so they can understand it even when it doesn't apply +to them. + +### Github and Pull Requests + +All of Opscode's open source cookbook projects are available on +[Github](http://www.github.com/opscode-cookbooks). + +We don't require you to use Github, and we will even take patch diffs +attached to tickets on the tracker. However Github has a lot of +convenient features, such as being able to see a diff of changes +between a pull request and the main repository quickly without +downloading the branch. + +If you do choose to use a pull request, please provide a link to the +pull request from the ticket __and__ a link to the ticket from the +pull request. Because pull requests only have two states, open and +closed, we can't easily filter pull requests that are waiting for a +reply from the author for various reasons. + +### More information + +Additional help with git is available on the +[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) +wiki page. + +## Functional and Unit Tests + +This cookbook is set up to run tests under +[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It +uses minitest-chef to run integration tests after the node has been +converged to verify that the state of the node. + +Test kitchen should run completely without exception using the default +[baseboxes provided by Opscode](https://github.com/opscode/bento). +Because Test Kitchen creates VirtualBox machines and runs through +every configuration in the Kitchenfile, it may take some time for +these tests to complete. + +If your changes are only for a specific recipe, run only its +configuration with Test Kitchen. If you are adding a new recipe, or +other functionality such as a LWRP or definition, please add +appropriate tests and ensure they run with Test Kitchen. + +If any don't pass, investigate them before submitting your patch. + +Any new feature should have unit tests included with the patch with +good code coverage to help protect it from future changes. Similarly, +patches that fix a bug or regression should have a _regression test_. +Simply put, this is a test that would fail without your patch but +passes with it. The goal is to ensure this bug doesn't regress in the +future. Consider a regular expression that doesn't match a certain +pattern that it should, so you provide a patch and a test to ensure +that the part of the code that uses this regular expression works as +expected. Later another contributor may modify this regular expression +in a way that breaks your use cases. The test you wrote will fail, +signalling to them to research your ticket and use case and accounting +for it. + +If you need help writing tests, please ask on the Chef Developer's +mailing list, or the #chef-hacking IRC channel. + +## Code Review + +Opscode regularly reviews code contributions and provides suggestions +for improvement in the code itself or the implementation. + +We find contributions by searching the ticket tracker for _resolved_ +tickets with a status of _fixed_. If we have feedback we will reopen +the ticket and you should resolve it again when you've made the +changes or have a response to our feedback. When we believe the patch +is ready to be merged, we will tag the _Code Reviewed_ field with +_Reviewed_. + +Depending on the project, these tickets are then merged within a week +or two, depending on the current release cycle. + +## Release Cycle + +The versioning for Opscode Cookbook projects is X.Y.Z. + +* X is a major release, which may not be fully compatible with prior + major releases +* Y is a minor release, which adds both new features and bug fixes +* Z is a patch release, which adds just bug fixes + +A released version of a cookbook will end in an even number, e.g. +"1.2.4" or "0.8.0". When development for the next version of the +cookbook begins, the "Z" patch number is incremented to the next odd +number, however the next release of the cookbook may be a major or +minor incrementing version. + +Releases of Opscode's cookbooks are usually announced on the Chef user +mailing list. Releases of several cookbooks may be batched together +and announced on the [Opscode Blog](http://www.opscode.com/blog). + +## Working with the community + +These resources will help you learn more about Chef and connect to +other members of the Chef community: + +* [chef](http://lists.opscode.com/sympa/info/chef) and + [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing + lists +* #chef and #chef-hacking IRC channels on irc.freenode.net +* [Community Cookbook site](http://community.opscode.com) +* [Chef wiki](http://wiki.opscode.com/display/chef) +* Opscode Chef [product page](http://www.opscode.com/chef) + + +## Cookbook Contribution Do's and Don't's + +Please do include tests for your contribution. If you need help, ask +on the +[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) +or the +[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). +Not all platforms that a cookbook supports may be supported by Test +Kitchen. Please provide evidence of testing your contribution if it +isn't trivial so we don't have to duplicate effort in testing. Chef +10.14+ "doc" formatted output is sufficient. + +Please do indicate new platform (families) or platform versions in the +commit message, and update the relevant ticket. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] - Updated pool resource to correctly + delete.' + +Please do use [foodcritic](http://acrmp.github.com/foodcritic) to +lint-check the cookbook. Except FC007, it should pass all correctness +rules. FC007 is okay as long as the dependent cookbooks are *required* +for the default behavior of the cookbook, such as to support an +uncommon platform, secondary recipe, etc. + +Please do ensure that your changes do not break or modify behavior for +other platforms supported by the cookbook. For example if your changes +are for Debian, make sure that they do not break on CentOS. + +Please do not modify the version number in the metadata.rb, Opscode +will select the appropriate version based on the release cycle +information above. + +Please do not update the CHANGELOG.md for a new version. Not all +changes to a cookbook may be merged and released in the same versions. +Opscode will update the CHANGELOG.md when releasing a new version of +the cookbook. diff --git a/chef/cookbooks/postgresql/LICENSE b/chef/cookbooks/postgresql/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/postgresql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/postgresql/README.md b/chef/cookbooks/postgresql/README.md new file mode 100644 index 0000000..f544eda --- /dev/null +++ b/chef/cookbooks/postgresql/README.md @@ -0,0 +1,438 @@ +Description +=========== + +Installs and configures PostgreSQL as a client or a server. + +Requirements +============ + +## Platforms + +* Debian, Ubuntu +* Red Hat/CentOS/Scientific (6.0+ required) - "EL6-family" +* Fedora +* SUSE + +Tested on: + +* Ubuntu 10.04, 11.10, 12.04 +* Red Hat 6.1, Scientific 6.1, CentOS 6.3 + +## Cookbooks + +Requires Opscode's `openssl` cookbook for secure password generation. + +Requires a C compiler and development headers in order to build the +`pg` RubyGem to provide Ruby bindings in the `ruby` recipe. + +Opscode's `build-essential` cookbook provides this functionality on +Debian, Ubuntu, and EL6-family. + +While not required, Opscode's `database` cookbook contains resources +and providers that can interact with a PostgreSQL database. This +cookbook is a dependency of database. + +Attributes +========== + +The following attributes are set based on the platform, see the +`attributes/default.rb` file for default values. + +* `node['postgresql']['version']` - version of postgresql to manage +* `node['postgresql']['dir']` - home directory of where postgresql + data and configuration lives. + +* `node['postgresql']['client']['packages']` - An array of package names + that should be installed on "client" systems. +* `node['postgresql']['server']['packages']` - An array of package names + that should be installed on "server" systems. +* `node['postgresql']['contrib']['packages']` - An array of package names + that could be installed on "server" systems for useful sysadmin tools. + +* `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo + by the PostgreSQL Global Development Group, which contains newer versions + of PostgreSQL. + +* `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo + by the PostgreSQL Global Development Group, which contains newer versions + of PostgreSQL. + +The following attributes are generated in +`recipe[postgresql::server]`. + +* `node['postgresql']['password']['postgres']` - randomly generated + password by the `openssl` cookbook's library. + (TODO: This is broken, as it disables the password.) + +Configuration +------------- + +The `postgresql.conf` and `pg_hba.conf` files are dynamically +generated from attributes. Each key in `node['postgresql']['config']` +is a postgresql configuration directive, and will be rendered in the +config file. For example, the attribute: + + node['postgresql']['config']['listen_address'] = 'localhost' + +Will result in the following line in the `postgresql.conf` file: + + listen_address = 'localhost' + +The attributes file contains default values for Debian and RHEL +platform families (per the `node['platform_family']`). These defaults +have disparity between the platforms because they were originally +extracted from the postgresql.conf files in the previous version of +this cookbook, which differed in their default config. The resulting +configuration files will be the same as before, but the content will +be dynamically rendered from the attributes. The helpful commentary +will no longer be present. You should consult the PostgreSQL +documentation for specific configuration details. + +See __Recipes__ `config_initdb` and `config_pgtune` below to +auto-generate many postgresql.conf settings. + +For values that are "on" or "off", they should be specified as literal +`true` or `false`. String values will be used with single quotes. Any +configuration option set to the literal `nil` will be skipped +entirely. All other values (e.g., numeric literals) will be used as +is. So for example: + + node.default['postgresql']['config']['logging_collector'] = true + node.default['postgresql']['config']['datestyle'] = 'iso, mdy' + node.default['postgresql']['config']['ident_file'] = nil + node.default['postgresql']['config']['port] = 5432 + +Will result in the following config lines: + + logging_collector = 'on' + datestyle = 'iso,mdy' + port = 5432 + +(no line printed for `ident_file` as it is `nil`) + +The `pg_hba.conf` file is dynamically generated from the +`node['postgresql']['pg_hba']` attribute. This attribute must be an +array of hashes, each hash containing the authorization data. As it is +an array, you can append to it in your own recipes. The hash keys in +the array must be symbols. Each hash will be written as a line in +`pg_hba.conf`. For example, this entry from +`node['postgresql']['pg_hba']`: + + {:comment => '# Optional comment', + :type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'} + +Will result in the following line in `pg_hba.conf`: + + # Optional comment + local all postgres md5 + +Use `nil` if the CIDR-ADDRESS should be empty (as above). +Don't provide a comment if none is desired in the `pg_hba.conf` file. + +Note that the following authorization rule is supplied automatically by +the cookbook template. The cookbook needs this to execute SQL in the +PostgreSQL server without supplying the clear-text password (which isn't +known by the cookbook). Therefore, your `node['postgresql']['pg_hba']` +attributes don't need to specify this authorization rule: + + # "local" is for Unix domain socket connections only + local all all ident + +(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1 +and above, which has the same effect.) + +Recipes +======= + +default +------- + +Includes the client recipe. + +client +------ + +Installs the packages defined in the +`node['postgresql']['client']['packages']` attribute. + +ruby +---- + +**NOTE** This recipe may not currently work when installing Chef with + the + ["Omnibus" full stack installer](http://opscode.com/chef/install) on + some platforms due to an incompatibility with OpenSSL. See + [COOK-1406](http://tickets.opscode.com/browse/COOK-1406). You can + build from source into the Chef omnibus installation to work around + this issue. + +Install the `pg` gem under Chef's Ruby environment so it can be used +in other recipes. The build-essential packages and postgresql client +packages will be installed during the compile phase, so that the +native extensions of `pg` can be compiled. + +server +------ + +Includes the `server_debian` or `server_redhat` recipe to get the +appropriate server packages installed and service managed. Also +manages the configuration for the server: + +* generates a strong default password (via `openssl`) for `postgres` + (TODO: This is broken, as it disables the password.) +* sets the password for postgres +* manages the `postgresql.conf` file. +* manages the `pg_hba.conf` file. + +server\_debian +-------------- + +Installs the postgresql server packages and sets up the service. You +should include the `postgresql::server` recipe, which will include +this on Debian platforms. + +server\_redhat +-------------- + +Manages the postgres user and group (with UID/GID 26, per RHEL package +conventions), installs the postgresql server packages, initializes the +database, and manages the postgresql service. You should include the +`postgresql::server` recipe, which will include this on RHEL/Fedora +platforms. + +config\_initdb +-------------- + +Takes locale and timezone settings from the system configuration. +This recipe creates `node.default['postgresql']['config']` attributes +that conform to the system's locale and timezone. In addition, this +recipe creates the same error reporting and logging settings that +`initdb` provided: a rotation of 7 days of log files named +postgresql-Mon.log, etc. + +The default attributes created by this recipe are easy to override with +normal attributes because of Chef attribute precedence. For example, +suppose a DBA wanted to keep log files indefinitely, rolling over daily +or when growing to 10MB. The Chef installation could include the +`postgresql::config_initdb` recipe for the locale and timezone settings, +but customize the logging settings with these node JSON attributes: + + "postgresql": { + "config": { + "log_rotation_age": "1d", + "log_rotation_size": "10MB", + "log_filename": "postgresql-%Y-%m-%d_%H%M%S.log" + } + } + +Credits: This `postgresql::config_initdb` recipe is based on algorithms +in the [source code](http://doxygen.postgresql.org/initdb_8c_source.html) +for the PostgreSQL `initdb` utility. + +config\_pgtune +-------------- + +Performance tuning. +Takes the wimpy default postgresql.conf and expands the database server +to be as powerful as the hardware it's being deployed on. This recipe +creates a baseline configuration of `node.default['postgresql']['config']` +attributes in the right general range for a dedicated Postgresql system. +Most installations won't need additional performance tuning. + +The only decision you need to make is to choose a `db_type` from the +following database workloads. (See the recipe code comments for more +detailed descriptions.) + + * "dw" -- Data Warehouse + * "oltp" -- Online Transaction Processing + * "web" -- Web Application + * "mixed" -- Mixed DW and OLTP characteristics + * "desktop" -- Not a dedicated database + +This recipe uses a performance model with three input parameters. +These node attributes are completely optional, but it is obviously +important to choose the `db_type` correctly: + + * `node['postgresql']['config_pgtune']['db_type']` -- + Specifies database type from the list of five choices above. + If not specified, the default is "mixed". + + * `node['postgresql']['config_pgtune']['max_connections']` -- + Specifies maximum number of connections expected. + If not specified, it depends on database type: + "web":200, "oltp":300, "dw":20, "mixed":80, "desktop":5 + + * `node['postgresql']['config_pgtune']['total_memory']` -- + Specifies total system memory in kB. (E.g., "49416564kB".) + If not specified, it will be taken from Ohai automatic attributes. + This could be used to tune a system that isn't a dedicated database. + +The default attributes created by this recipe are easy to override with +normal attributes because of Chef attribute precedence. For example, if +you are running application benchmarks to try different buffer cache +sizes, you would experiment with this node JSON attribute: + + "postgresql": { + "config": { + "shared_buffers": "3GB" + } + } + +Note that the recipe uses `max_connections` in its computations. If +you want to override that setting, you should specify +`node['postgresql']['config_pgtune']['max_connections']` instead of +`node['postgresql']['config']['max_connections']`. + +Credits: This `postgresql::config_pgtune` recipe is based on the +[pgtune python script](https://github.com/gregs1104/pgtune) +developed by +[Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html) +and +[other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com). + +contrib +------- + +Installs the packages defined in the +`node['postgresql']['contrib']['packages']` attribute. The contrib +directory of the PostgreSQL distribution includes porting tools, +analysis utilities, and plug-in features that database engineers often +require. Some (like `pgbench`) are executable. Others (like +`pg_buffercache`) would need to be installed into the database. + +Also installs any contrib module extensions defined in the +`node['postgresql']['contrib']['extensions']` attribute. These will be +available in any subsequently created databases in the cluster, because +they will be installed into the `template1` database using the +`CREATE EXTENSION` command. For example, it is often necessary/helpful +for problem troubleshooting and maintenance planning to install the +views and functions in these [standard instrumentation extensions] +(http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com): + + node['postgresql']['contrib']['extensions'] = [ + "pageinspect", + "pg_buffercache", + "pg_freespacemap", + "pgrowlocks", + "pg_stat_statements", + "pgstattuple" + ] + +Note that the `pg_stat_statements` view only works if `postgresql.conf` +loads its shared library, which can be done with this node attribute: + + node['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements' + +apt\_pgdg\_postgresql +---------------------- + +Enables the PostgreSQL Global Development Group yum repository +maintained by Devrim Gündüz for updated PostgreSQL packages. +(The PGDG is the groups that develops PostgreSQL.) +Automatically included if the `node['postgresql']['enable_pgdg_apt']` +attribute is true. Also set the +`node['postgresql']['client']['packages']` and +`node['postgresql']['server]['packages']` to the list of packages to +use from this repository, and set the `node['postgresql']['version']` +attribute to the version to use (e.g., "9.2"). + +yum\_pgdg\_postgresql +--------------------- + +Enables the PostgreSQL Global Development Group yum repository +maintained by Devrim Gündüz for updated PostgreSQL packages. +(The PGDG is the groups that develops PostgreSQL.) +Automatically included if the `node['postgresql']['enable_pgdg_yum']` +attribute is true. Also use `override_attributes` to set a number of +values that will need to have embedded version numbers. For example: + + node['postgresql']['enable_pgdg_yum'] = true + node['postgresql']['version'] = "9.2" + node['postgresql']['dir'] = "/var/lib/pgsql/9.2/data" + node['postgresql']['client']['packages'] = ["postgresql92", "postgresql92-devel"] + node['postgresql']['server']['packages'] = ["postgresql92-server"] + node['postgresql']['server']['service_name'] = "postgresql-9.2" + node['postgresql']['contrib']['packages'] = ["postgresql92-contrib"] + +You may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes +to pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php). + +Resources/Providers +=================== + +See the [database](http://community.opscode.com/cookbooks/database) +for resources and providers that can be used for managing PostgreSQL +users and databases. + +Usage +===== + +On systems that need to connect to a PostgreSQL database, add to a run +list `recipe[postgresql]` or `recipe[postgresql::client]`. + +On systems that should be PostgreSQL servers, use +`recipe[postgresql::server]` on a run list. This recipe does set a +password for the `postgres` user. +If you're using `chef server`, if the attribute +`node['postgresql']['password']['postgres']` is not found, +the recipe generates a random password and performs a node.save. +(TODO: This is broken, as it disables the password.) +If you're using `chef-solo`, you'll need +to set the attribute `node['postgresql']['password']['postgres']` in +your node's `json_attribs` file or in a role. + +On Debian family systems, SSL will be enabled, as the packages on +Debian/Ubuntu also generate the SSL certificates. If you use another +platform and wish to use SSL in postgresql, then generate your SSL +certificates and distribute them in your own cookbook, and set the +`node['postgresql']['config']['ssl']` attribute to true in your +role/cookboook/node. + +Chef Solo Note +============== + +The following node attribute is stored on the Chef Server when using +`chef-client`. Because `chef-solo` does not connect to a server or +save the node object at all, to have the password persist across +`chef-solo` runs, you must specify them in the `json_attribs` file +used. For Example: + + { + "postgresql": { + "password": { + "postgres": "iloverandompasswordsbutthiswilldo" + } + }, + "run_list": ["recipe[postgresql::server]"] + } + +That should actually be the "encrypted password" instead of cleartext, +so you should generate it as an md5 hash using the PostgreSQL algorithm. + +* You could copy the md5-hashed password from an existing postgres +database if you have `postgres` access and want to use the same password:
+`select * from pg_shadow where usename='postgres';` +* You can run this from any postgres database session to use a new password:
+`select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');` +* You can run this from a linux commandline:
+`echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'` + +License and Author +================== + +- Author:: Joshua Timberman () +- Author:: Lamont Granquist () +- Author:: Chris Roberts () +- Author:: David Crane () + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/postgresql/TESTING.md b/chef/cookbooks/postgresql/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/postgresql/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/postgresql/attributes/default.rb b/chef/cookbooks/postgresql/attributes/default.rb new file mode 100644 index 0000000..fd49c39 --- /dev/null +++ b/chef/cookbooks/postgresql/attributes/default.rb @@ -0,0 +1,419 @@ +# +# Cookbook Name:: postgresql +# Attributes:: postgresql +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform'] +when "debian" + + case + when node['platform_version'].to_f < 6.0 # All 5.X + default['postgresql']['version'] = "8.3" + when node['platform_version'].to_f < 7.0 # All 6.X + default['postgresql']['version'] = "8.4" + else + default['postgresql']['version'] = "9.1" + end + + default['postgresql']['dir'] = "/etc/postgresql/#{node['postgresql']['version']}/main" + case + when node['platform_version'].to_f < 6.0 # All 5.X + default['postgresql']['server']['service_name'] = "postgresql-#{node['postgresql']['version']}" + else + default['postgresql']['server']['service_name'] = "postgresql" + end + + default['postgresql']['client']['packages'] = %w{postgresql-client libpq-dev} + default['postgresql']['server']['packages'] = %w{postgresql} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + +when "ubuntu" + + case + when node['platform_version'].to_f <= 9.04 + default['postgresql']['version'] = "8.3" + when node['platform_version'].to_f <= 11.04 + default['postgresql']['version'] = "8.4" + else + default['postgresql']['version'] = "9.1" + end + + default['postgresql']['dir'] = "/etc/postgresql/#{node['postgresql']['version']}/main" + case + when (node['platform_version'].to_f <= 10.04) && (! node['postgresql']['enable_pgdg_apt']) + default['postgresql']['server']['service_name'] = "postgresql-#{node['postgresql']['version']}" + else + default['postgresql']['server']['service_name'] = "postgresql" + end + + default['postgresql']['client']['packages'] = %w{postgresql-client libpq-dev} + default['postgresql']['server']['packages'] = %w{postgresql} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + +when "fedora" + + if node['platform_version'].to_f <= 12 + default['postgresql']['version'] = "8.3" + else + default['postgresql']['version'] = "8.4" + end + + default['postgresql']['dir'] = "/var/lib/pgsql/data" + default['postgresql']['client']['packages'] = %w{postgresql-devel} + default['postgresql']['server']['packages'] = %w{postgresql-server} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + default['postgresql']['server']['service_name'] = "postgresql" + +when "amazon" + + default['postgresql']['version'] = "8.4" + default['postgresql']['dir'] = "/var/lib/pgsql/data" + default['postgresql']['client']['packages'] = %w{postgresql-devel} + default['postgresql']['server']['packages'] = %w{postgresql-server} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + default['postgresql']['server']['service_name'] = "postgresql" + +when "redhat", "centos", "scientific", "oracle" + + default['postgresql']['version'] = "8.4" + default['postgresql']['dir'] = "/var/lib/pgsql/data" + + if node['platform_version'].to_f >= 6.0 + default['postgresql']['client']['packages'] = %w{postgresql-devel} + default['postgresql']['server']['packages'] = %w{postgresql-server} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + else + default['postgresql']['client']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-devel"] + default['postgresql']['server']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-server"] + default['postgresql']['contrib']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-contrib"] + end + default['postgresql']['server']['service_name'] = "postgresql" + +when "suse" + + if node['platform_version'].to_f <= 11.1 + default['postgresql']['version'] = "8.3" + else + default['postgresql']['version'] = "9.0" + end + + default['postgresql']['dir'] = "/var/lib/pgsql/data" + default['postgresql']['client']['packages'] = %w{postgresql-devel} + default['postgresql']['server']['packages'] = %w{postgresql-server} + default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} + default['postgresql']['server']['service_name'] = "postgresql" + +else + default['postgresql']['version'] = "8.4" + default['postgresql']['dir'] = "/etc/postgresql/#{node['postgresql']['version']}/main" + default['postgresql']['client']['packages'] = ["postgresql"] + default['postgresql']['server']['packages'] = ["postgresql"] + default['postgresql']['contrib']['packages'] = ["postgresql"] + default['postgresql']['server']['service_name'] = "postgresql" +end + +# These defaults have disparity between which postgresql configuration +# settings are used because they were extracted from the original +# configuration files that are now removed in favor of dynamic +# generation. +# +# While the configuration ends up being the same as the default +# in previous versions of the cookbook, the content of the rendered +# template will change, and this will result in service notification +# if you upgrade the cookbook on existing systems. +# +# The ssl config attribute is generated in the recipe to avoid awkward +# merge/precedence order during the Chef run. +case node['platform_family'] +when 'debian' + default['postgresql']['config']['data_directory'] = "/var/lib/postgresql/#{node['postgresql']['version']}/main" + default['postgresql']['config']['hba_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_hba.conf" + default['postgresql']['config']['ident_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_ident.conf" + default['postgresql']['config']['external_pid_file'] = "/var/run/postgresql/#{node['postgresql']['version']}-main.pid" + default['postgresql']['config']['listen_addresses'] = 'localhost' + default['postgresql']['config']['port'] = 5432 + default['postgresql']['config']['max_connections'] = 100 + default['postgresql']['config']['unix_socket_directory'] = '/var/run/postgresql' + default['postgresql']['config']['shared_buffers'] = '24MB' + default['postgresql']['config']['max_fsm_pages'] = 153600 if node['postgresql']['version'].to_f < 8.4 + default['postgresql']['config']['log_line_prefix'] = '%t ' + default['postgresql']['config']['datestyle'] = 'iso, mdy' + default['postgresql']['config']['default_text_search_config'] = 'pg_catalog.english' + default['postgresql']['config']['ssl'] = true +when 'rhel', 'fedora', 'suse' + default['postgresql']['config']['listen_addresses'] = 'localhost' + default['postgresql']['config']['max_connections'] = 100 + default['postgresql']['config']['shared_buffers'] = '32MB' + default['postgresql']['config']['logging_collector'] = true + default['postgresql']['config']['log_directory'] = 'pg_log' + default['postgresql']['config']['log_filename'] = 'postgresql-%a.log' + default['postgresql']['config']['log_truncate_on_rotation'] = true + default['postgresql']['config']['log_rotation_age'] = '1d' + default['postgresql']['config']['log_rotation_size'] = 0 + default['postgresql']['config']['datestyle'] = 'iso, mdy' + default['postgresql']['config']['lc_messages'] = 'en_US.UTF-8' + default['postgresql']['config']['lc_monetary'] = 'en_US.UTF-8' + default['postgresql']['config']['lc_numeric'] = 'en_US.UTF-8' + default['postgresql']['config']['lc_time'] = 'en_US.UTF-8' + default['postgresql']['config']['default_text_search_config'] = 'pg_catalog.english' +end + +default['postgresql']['pg_hba'] = [ + {:type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'ident'}, + {:type => 'local', :db => 'all', :user => 'all', :addr => nil, :method => 'ident'}, + {:type => 'host', :db => 'all', :user => 'all', :addr => '127.0.0.1/32', :method => 'md5'}, + {:type => 'host', :db => 'all', :user => 'all', :addr => '::1/128', :method => 'md5'} +] + +default['postgresql']['password'] = Hash.new + +default['postgresql']['enable_pgdg_apt'] = false + +case node['platform_family'] +when 'debian' + default['postgresql']['pgdg']['release_apt_codename'] = node['lsb']['codename'] +end + +default['postgresql']['enable_pgdg_yum'] = false + +# The PostgreSQL RPM Building Project built repository RPMs for easy +# access to the PGDG yum repositories. Links to RPMs for installation +# on the supported version/platform combinations are listed at +# http://yum.postgresql.org/repopackages.php, and the links for +# PostgreSQL 8.4, 9.0, 9.1 and 9.2 (from 2013-01-15) are captured below. +# +# The correct RPM for installing /etc/yum.repos.d is based on: +# * the attribute configuring the desired Postgres Software: +# node['postgresql']['version'] e.g., "9.1" +# * the chef ohai description of the target Operating System: +# node['platform'] e.g., "centos" +# node['platform_version'] e.g., "5.7", truncated as "5" +# node['kernel']['machine'] e.g., "i386" or "x86_64" +default['postgresql']['pgdg']['repo_rpm_url'] = { + "9.2" => { + "centos" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/pgdg-centos92-9.2-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/pgdg-centos92-9.2-6.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/pgdg-centos92-9.2-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/pgdg-centos92-9.2-6.noarch.rpm" + } + }, + "redhat" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/pgdg-redhat92-9.2-7.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/pgdg-redhat92-9.2-7.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/pgdg-redhat92-9.2-7.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/pgdg-redhat92-9.2-7.noarch.rpm" + } + }, + "scientific" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/pgdg-sl92-9.2-8.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/pgdg-sl92-9.2-8.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/pgdg-sl92-9.2-8.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/pgdg-sl92-9.2-8.noarch.rpm" + } + }, + "fedora" => { + "17" => { + "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-17-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" + }, + "16" => { + "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-16-i386/pgdg-fedora92-9.2-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-16-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" + }, + "15" => { + "i386" => "http://yum.postgresql.org/9.2/fedora/fedora-15-i386/pgdg-fedora92-9.2-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.2/fedora/fedora-15-x86_64/pgdg-fedora92-9.2-5.noarch.rpm" + } + } + }, + "9.1" => { + "centos" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-6-i386/pgdg-centos91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-5-x86_64/pgdg-centos91-9.1-4.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-5-i386/pgdg-centos91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-5-x86_64/pgdg-centos91-9.1-4.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-4-i386/pgdg-centos91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-4-x86_64/pgdg-centos91-9.1-4.noarch.rpm" + } + }, + "redhat" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-6-i386/pgdg-redhat91-9.1-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-6-x86_64/pgdg-redhat91-9.1-5.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-5-i386/pgdg-redhat91-9.1-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-5-x86_64/pgdg-redhat91-9.1-5.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-4-i386/pgdg-redhat-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-4-x86_64/pgdg-redhat-9.1-4.noarch.rpm" + } + }, + "scientific" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-6-i386/pgdg-sl91-9.1-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-6-x86_64/pgdg-sl91-9.1-6.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.1/redhat/rhel-5-i386/pgdg-sl91-9.1-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/redhat/rhel-5-x86_64/pgdg-sl91-9.1-6.noarch.rpm" + } + }, + "fedora" => { + "16" => { + "i386" => "http://yum.postgresql.org/9.1/fedora/fedora-16-i386/pgdg-fedora91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/fedora/fedora-16-x86_64/pgdg-fedora91-9.1-4.noarch.rpm" + }, + "15" => { + "i386" => "http://yum.postgresql.org/9.1/fedora/fedora-15-i386/pgdg-fedora91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/fedora/fedora-15-x86_64/pgdg-fedora91-9.1-4.noarch.rpm" + }, + "14" => { + "i386" => "http://yum.postgresql.org/9.1/fedora/fedora-14-i386/pgdg-fedora91-9.1-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.1/fedora/fedora-14-x86_64/pgdg-fedora-9.1-2.noarch.rpm" + } + } + }, + "9.0" => { + "centos" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-6-i386/pgdg-centos90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-6-x86_64/pgdg-centos90-9.0-5.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-5-i386/pgdg-centos90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-5-x86_64/pgdg-centos90-9.0-5.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-4-i386/pgdg-centos90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-4-x86_64/pgdg-centos90-9.0-5.noarch.rpm" + } + }, + "redhat" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-6-i386/pgdg-redhat90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-6-x86_64/pgdg-redhat90-9.0-5.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-5-i386/pgdg-redhat90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-5-x86_64/pgdg-redhat90-9.0-5.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-4-i386/pgdg-redhat90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-4-x86_64/pgdg-redhat90-9.0-5.noarch.rpm" + } + }, + "scientific" => { + "6" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-6-i386/pgdg-sl90-9.0-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-6-x86_64/pgdg-sl90-9.0-6.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/9.0/redhat/rhel-5-i386/pgdg-sl90-9.0-6.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/redhat/rhel-5-x86_64/pgdg-sl90-9.0-6.noarch.rpm" + } + }, + "fedora" => { + "15" => { + "i386" => "http://yum.postgresql.org/9.0/fedora/fedora-15-i386/pgdg-fedora90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/fedora/fedora-15-x86_64/pgdg-fedora90-9.0-5.noarch.rpm" + }, + "14" => { + "i386" => "http://yum.postgresql.org/9.0/fedora/fedora-14-i386/pgdg-fedora90-9.0-5.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/9.0/fedora/fedora-14-x86_64/pgdg-fedora90-9.0-5.noarch.rpm" + } + } + }, + "8.4" => { + "centos" => { + "6" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-6-i386/pgdg-centos-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-6-x86_64/pgdg-centos-8.4-3.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-5-i386/pgdg-centos-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-5-x86_64/pgdg-centos-8.4-3.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-4-i386/pgdg-centos-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-4-x86_64/pgdg-centos-8.4-3.noarch.rpm" + } + }, + "redhat" => { + "6" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-6-i386/pgdg-redhat-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-6-x86_64/pgdg-redhat-8.4-3.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-5-i386/pgdg-redhat-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-5-x86_64/pgdg-redhat-8.4-3.noarch.rpm" + }, + "4" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-4-i386/pgdg-redhat-8.4-3.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-4-x86_64/pgdg-redhat-8.4-3.noarch.rpm" + } + }, + "scientific" => { + "6" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-6-i386/pgdg-sl84-8.4-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-6-x86_64/pgdg-sl84-8.4-4.noarch.rpm" + }, + "5" => { + "i386" => "http://yum.postgresql.org/8.4/redhat/rhel-5-i386/pgdg-sl-8.4-4.noarch.rpm", + "x86_64" => "http://yum.postgresql.org/8.4/redhat/rhel-5-x86_64/pgdg-sl-8.4-4.noarch.rpm" + } + }, + "fedora" => { + "14" => { + "i386" => "http://yum.postgresql.org/8.4/fedora/fedora-14-i386/", + "x86_64" => "http://yum.postgresql.org/8.4/fedora/fedora-14-x86_64/" + }, + "13" => { + "i386" => "http://yum.postgresql.org/8.4/fedora/fedora-13-i386/", + "x86_64" => "http://yum.postgresql.org/8.4/fedora/fedora-13-x86_64/" + }, + "12" => { + "i386" => "http://yum.postgresql.org/8.4/fedora/fedora-12-i386/", + "x86_64" => "http://yum.postgresql.org/8.4/fedora/fedora-12-x86_64/" + }, + "8" => { + "i386" => "http://yum.postgresql.org/8.4/fedora/fedora-8-i386/", + "x86_64" => "http://yum.postgresql.org/8.4/fedora/fedora-8-x86_64/" + }, + "7" => { + "i386" => "http://yum.postgresql.org/8.4/fedora/fedora-7-i386/", + "x86_64" => "http://yum.postgresql.org/8.4/fedora/fedora-7-x86_64/" + } + } + }, +}; + diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb new file mode 100644 index 0000000..eb3084a --- /dev/null +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb @@ -0,0 +1,39 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'postgresql::apt_pgdg_postgresql' do + include Helpers::Postgresql + + it 'removes the Pitti PPA sources.list' do + skip unless %w{debian}.include?(node['platform_family']) + file("/etc/apt/sources.list.d/pitti-postgresql-ppa").wont_exist + end + it 'creates the PGDG apt sources.list' do + skip unless %w{debian}.include?(node['platform_family']) + file("/etc/apt/sources.list.d/apt.postgresql.org.list").must_exist + end + + it 'installs postgresql-client-9.2' do + package("postgresql-client-9.2").must_be_installed + end + + it 'makes psql version 9.2 available' do + psql = shell_out("psql --version") + assert psql.stdout.include?("psql (PostgreSQL) 9.2") + end +end diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/default_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..8acbabf --- /dev/null +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/default_test.rb @@ -0,0 +1,27 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'postgresql::default' do + include Helpers::Postgresql + + it 'installs the postgresql client packages' do + node['postgresql']['client']['packages'].each do |pkg| + package(pkg).must_be_installed + end + end +end diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb new file mode 100644 index 0000000..3b3649f --- /dev/null +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: postgresql_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'postgresql::ruby' do + include Helpers::Postgresql + + it 'installs the pg gem in Chefs ruby environment' do + assert Gem::Specification.all_names.grep("pg-.*") + end +end diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb new file mode 100644 index 0000000..bd9fdbc --- /dev/null +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/server_test.rb @@ -0,0 +1,43 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe 'postgresql::server' do + include Helpers::Postgresql + + it 'installs the postgresql server packages' do + node['postgresql']['server']['packages'].each do |pkg| + package(pkg).must_be_installed + end + end + + it 'runs the postgresql service' do + service("postgresql").must_be_running + end + + it 'can connect to postgresql' do + require 'pg' + conn = PG::Connection.new( + :host => 'localhost', + :port => '5432', + :password => node['postgresql']['password']['postgres'], + :user => "postgres" + ) + assert_match(/localhost/, conn.host) + end + +end diff --git a/chef/cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..fd8fcea --- /dev/null +++ b/chef/cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: postgresql_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Helpers + module Postgresql + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + end +end diff --git a/chef/cookbooks/postgresql/libraries/default.rb b/chef/cookbooks/postgresql/libraries/default.rb new file mode 100644 index 0000000..8bff889 --- /dev/null +++ b/chef/cookbooks/postgresql/libraries/default.rb @@ -0,0 +1,376 @@ +# +# Cookbook Name:: postgresql +# Library:: default +# Author:: David Crane () +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Chef::Mixin::ShellOut + +module Opscode + module PostgresqlHelpers + +####### +# Function to truncate value to 4 significant bits, render human readable. +# Used in recipes/config_initdb.rb to set this attribute: +# +# The memory settings (shared_buffers, effective_cache_size, work_mem, +# maintenance_work_mem and wal_buffers) will be rounded down to keep +# the 4 most significant bits, so that SHOW will be likely to use a +# larger divisor. The output is actually a human readable string that +# ends with "GB", "MB" or "kB" if over 1023, exactly what Postgresql +# will expect in a postgresql.conf setting. The output may be up to +# 6.25% less than the original value because of the rounding. +def binaryround(value) + + # Keep a multiplier which grows through powers of 1 + multiplier = 1 + + # Truncate value to 4 most significant bits + while value >= 16 + value = (value / 2).floor + multiplier = multiplier * 2 + end + + # Factor any remaining powers of 2 into the multiplier + while value == 2*((value / 2).floor) + value = (value / 2).floor + multiplier = multiplier * 2 + end + + # Factor enough powers of 2 back into the value to + # leave the multiplier as a power of 1024 that can + # be represented as units of "GB", "MB" or "kB". + if multiplier >= 1024*1024*1024 + while multiplier > 1024*1024*1024 + value = 2*value + multiplier = (multiplier/2).floor + end + multiplier = 1 + units = "GB" + + elsif multiplier >= 1024*1024 + while multiplier > 1024*1024 + value = 2*value + multiplier = (multiplier/2).floor + end + multiplier = 1 + units = "MB" + + elsif multiplier >= 1024 + while multiplier > 1024 + value = 2*value + multiplier = (multiplier/2).floor + end + multiplier = 1 + units = "kB" + + else + units = "" + end + + # Now we can return a nice human readable string. + return "#{multiplier * value}#{units}" +end + +####### +# Locale Configuration + +# Function to test the date order. +# Used in recipes/config_initdb.rb to set this attribute: +# node.default['postgresql']['config']['datestyle'] +def locale_date_order + # Test locale conversion of mon=11, day=22, year=33 + testtime = DateTime.new(2033,11,22,0,0,0,"-00:00") + #=> # + + # %x - Preferred representation for the date alone, no time + res = testtime.strftime("%x") + + if res.nil? + return 'mdy' + end + + posM = res.index("11") + posD = res.index("22") + posY = res.index("33") + + if (posM.nil? || posD.nil? || posY.nil?) + return 'mdy' + elseif (posY < posM && posM < posD) + return 'ymd' + elseif (posD < posM) + return 'dmy' + else + return 'mdy' + end +end + +####### +# Timezone Configuration +require 'find' + +# Function to determine where the system stored shared timezone data. +# Used in recipes/config_initdb.rb to detemine where it should have +# select_default_timezone(tzdir) search. +def pg_TZDIR() + # System time zone conversions are controlled by a timezone data file + # identified through environment variables (TZ and TZDIR) and/or file + # and directory naming conventions specific to the Linux distribution. + # Each of these timezone names will have been loaded into the PostgreSQL + # pg_timezone_names view by the package maintainer. + # + # Instead of using the timezone name configured as the system default, + # the PostgreSQL server uses ones named in postgresql.conf settings + # (timezone and log_timezone). The initdb utility does initialize those + # settings to the timezone name that corresponds to the system default. + # + # The system's timezone name is actually a filename relative to the + # shared zoneinfo directory. That is usually /usr/share/zoneinfo, but + # it was /usr/lib/zoneinfo in older distributions and can be anywhere + # if specified by the environment variable TZDIR. The tzset(3) manpage + # seems to indicate the following precedence: + tzdir = nil + if ::File.directory?("/usr/lib/zoneinfo") + tzdir = "/usr/lib/zoneinfo" + else + share_path = [ ENV['TZDIR'], "/usr/share/zoneinfo" ].compact.first + if ::File.directory?(share_path) + tzdir = share_path + end + end + return tzdir +end + +####### +# Function to support select_default_timezone(tzdir), which is +# used in recipes/config_initdb.rb. +def validate_zone(tzname) + # PostgreSQL does not support leap seconds, so this function tests + # the usual Linux tzname convention to avoid a misconfiguration. + # Assume that the tzdata package maintainer has kept all timezone + # data files with support for leap seconds is kept under the + # so-named "right/" subdir of the shared zoneinfo directory. + # + # The original PostgreSQL initdb is not Unix-specific, so it did a + # very complicated, thorough test in its pg_tz_acceptable() function + # that I could not begin to understand how to do in ruby :). + # + # Testing the tzname is good enough, since a misconfiguration + # will result in an immediate fatal error when the PostgreSQL + # service is started, with pgstartup.log messages such as: + # LOG: time zone "right/US/Eastern" appears to use leap seconds + # DETAIL: PostgreSQL does not support leap seconds. + + if tzname.index("right/") == 0 + return false + else + return true + end +end + +# Function to support select_default_timezone(tzdir), which is +# used in recipes/config_initdb.rb. +def scan_available_timezones(tzdir) + # There should be an /etc/localtime zoneinfo file that is a link to + # (or a copy of) a timezone data file under tzdir, which should have + # been installed under the "share" directory by the tzdata package. + # + # The initdb utility determines which shared timezone file is being + # used as the system's default /etc/localtime. The timezone name is + # the timezone file path relative to the tzdir. + + bestzonename = nil + + if (tzdir.nil?) + Chef::Log.error("The zoneinfo directory not found (looked for /usr/share/zoneinfo and /usr/lib/zoneinfo)") + elsif !::File.exists?("/etc/localtime") + Chef::Log.error("The system zoneinfo file not found (looked for /etc/localtime)") + elsif ::File.directory?("/etc/localtime") + Chef::Log.error("The system zoneinfo file not found (/etc/localtime is a directory instead)") + elsif ::File.symlink?("/etc/localtime") + # PostgreSQL initdb doesn't use the symlink target, but this + # certainly will make sense to any system administrator. A full + # scan of the tzdir to find the shortest filename could result + # "US/Eastern" instead of "America/New_York" as bestzonename, + # in spite of what the sysadmin had specified in the symlink. + # (There are many duplicates under tzdir, with the same timezone + # content appearing as an average of 2-3 different file names.) + path = ::File.readlink("/etc/localtime") + bestzonename = path.gsub("#{tzdir}/","") + else # /etc/localtime is a file, so scan for it under tzdir + localtime_content = File.read("/etc/localtime") + + Find.find(tzdir) do |path| + # Only consider files (skip directories or symlinks) + if !::File.directory?(path) && !::File.symlink?(path) + # Ignore any file named "posixrules" or "localtime" + if ::File.basename(path) != "posixrules" && ::File.basename(path) != "localtime" + # Do consider if content exactly matches /etc/localtime. + if localtime_content == File.read(path) + tzname = path.gsub("#{tzdir}/","") + if validate_zone(tzname) + if (bestzonename.nil? || + tzname.length < bestzonename.length || + (tzname.length == bestzonename.length && + (tzname <=> bestzonename) < 0) + ) + bestzonename = tzname + end + end + end + end + end + end + end + + return bestzonename +end + +# Function to support select_default_timezone(tzdir), which is +# used in recipes/config_initdb.rb. +def identify_system_timezone(tzdir) + resultbuf = scan_available_timezones(tzdir) + + if !resultbuf.nil? + # Ignore Olson's rather silly "Factory" zone; use GMT instead + if (resultbuf <=> "Factory") == 0 + resultbuf = nil + end + + else + # Did not find the timezone. Fallback to use a GMT zone. Note that the + # Olson timezone database names the GMT-offset zones in POSIX style: plus + # is west of Greenwich. + testtime = DateTime.now + std_ofs = testtime.strftime("%:z").split(":")[0].to_i + + resultbuf = [ + "Etc/GMT", + (-std_ofs > 0) ? "+" : "", + (-std_ofs).to_s + ].join('') + end + + return resultbuf +end + +####### +# Function to determine the name of the system's default timezone. +# Used in recipes/config_initdb.rb to set these attributes: +# node.default['postgresql']['config']['log_timezone'] +# node.default['postgresql']['config']['timezone'] +def select_default_timezone(tzdir) + + system_timezone = nil + + # Check TZ environment variable + tzname = ENV['TZ'] + if !tzname.nil? && !tzname.empty? && validate_zone(tzname) + system_timezone = tzname + + else + # Nope, so try to identify system timezone from /etc/localtime + tzname = identify_system_timezone(tzdir) + if validate_zone(tzname) + system_timezone = tzname + end + end + + return system_timezone +end + +####### +# Function to determine the name of the system's default timezone. +def get_result_orig(query) + # query could be a String or an Array of String + if (query.is_a?(String)) + stdin = query + else + stdin = query.join("\n") + end + @get_result ||= begin + cmd = shell_out("cat", :input => stdin) + cmd.stdout + end +end + +####### +# Function to execute an SQL statement in the template1 database. +# Input: Query could be a single String or an Array of String. +# Output: A String with |-separated columns and \n-separated rows. +# Note an empty output could mean psql couldn't connect. +# This is easiest for 1-field (1-row, 1-col) results, otherwise +# it will be complex to parse the results. +def execute_sql(query) + # query could be a String or an Array of String + statement = query.is_a?(String) ? query : query.join("\n") + @execute_sql ||= begin + cmd = shell_out("psql -q --tuples-only --no-align -d template1 -f -", + :user => "postgres", + :input => statement + ) + # If psql fails, generally the postgresql service is down. + # Instead of aborting chef with a fatal error, let's just + # pass these non-zero exitstatus back as empty cmd.stdout. + if (cmd.exitstatus() == 0 and !cmd.stderr.empty?) + # An SQL failure is still a zero exitstatus, but then the + # stderr explains the error, so let's rais that as fatal. + Chef::Log.fatal("psql failed executing this SQL statement:\n#{statement}") + Chef::Log.fatal(cmd.stderr) + raise "SQL ERROR" + end + cmd.stdout.chomp + end +end + +####### +# Function to determine if a standard contrib extension is already installed. +# Input: Extension name +# Output: true or false +# Best use as a not_if gate on bash "install-#{pg_ext}-extension" resource. +def extension_installed?(pg_ext) + @extension_installed ||= begin + installed=execute_sql("select 'installed' from pg_extension where extname = '#{pg_ext}';") + installed =~ /^installed$/ + end +end + +###################################### +# Function to build information needed to install RPM for PGDG yum repository, +# since PGDG supports several versions of PostgreSQL, platforms, platform versions +# and architectures. +# Links to RPMs for installation are in an attribute so that new versions/platforms +# can be more easily added. (See attributes/default.rb) +def pgdgrepo_rpm_info + repo_rpm_url = node['postgresql']['pgdg']['repo_rpm_url']. + fetch(node['postgresql']['version']). # e.g., fetch for "9.1" + fetch(node['platform']). # e.g., fetch for "centos" + fetch(node['platform_version'].to_f.to_i.to_s). # e.g., fetch for "5" (truncated "5.7") + fetch(node['kernel']['machine']) # e.g., fetch for "i386" or "x86_64" + + # Extract the filename portion from the URL for the PGDG repository RPM. + # E.g., repo_rpm_filename = "pgdg-centos92-9.2-6.noarch.rpm" + repo_rpm_filename = File.basename(repo_rpm_url) + + # Extract the package name from the URL for the PGDG repository RPM. + # E.g., repo_rpm_package = "pgdg-centos92" + repo_rpm_package = repo_rpm_filename.split(/-/,3)[0..1].join('-') + + return [ repo_rpm_url, repo_rpm_filename, repo_rpm_package ] +end + +# End the Opscode::PostgresqlHelpers module + end +end diff --git a/chef/cookbooks/postgresql/metadata.rb b/chef/cookbooks/postgresql/metadata.rb new file mode 100644 index 0000000..755000e --- /dev/null +++ b/chef/cookbooks/postgresql/metadata.rb @@ -0,0 +1,25 @@ +name "postgresql" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures postgresql for clients or servers" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "3.0.5" +recipe "postgresql", "Includes postgresql::client" +recipe "postgresql::ruby", "Installs pg gem for Ruby bindings" +recipe "postgresql::client", "Installs postgresql client package(s)" +recipe "postgresql::server", "Installs postgresql server packages, templates" +recipe "postgresql::server_redhat", "Installs postgresql server packages, redhat family style" +recipe "postgresql::server_debian", "Installs postgresql server packages, debian family style" + +%w{ubuntu debian fedora suse amazon}.each do |os| + supports os +end + +%w{redhat centos scientific oracle}.each do |el| + supports el, ">= 6.0" +end + +depends "apt" +depends "build-essential" +depends "openssl" diff --git a/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb b/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb new file mode 100644 index 0000000..cf91cef --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb @@ -0,0 +1,18 @@ +if not %w(etch lenny lucid precise sid squeeze wheezy).include? node['postgresql']['pgdg']['release_apt_codename'] + raise "Not supported release by PGDG apt repository" +end + +include_recipe 'apt' + +file "remove deprecated Pitti PPA apt repository" do + action :delete + path "/etc/apt/sources.list.d/pitti-postgresql-ppa" +end + +apt_repository 'apt.postgresql.org' do + uri 'http://apt.postgresql.org/pub/repos/apt' + distribution "#{node['postgresql']['pgdg']['release_apt_codename']}-pgdg" + components %w(main) + key 'http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc' + action :add +end diff --git a/chef/cookbooks/postgresql/recipes/client.rb b/chef/cookbooks/postgresql/recipes/client.rb new file mode 100644 index 0000000..e659083 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/client.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: postgresql +# Recipe:: client +# +# Author:: Joshua Timberman () +# Author:: Lamont Granquist () +# Copyright 2009-2011 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(node['postgresql']['enable_pgdg_apt']) + include_recipe 'postgresql::apt_pgdg_postgresql' +end + +if(node['postgresql']['enable_pgdg_yum']) + include_recipe 'postgresql::yum_pgdg_postgresql' +end + +node['postgresql']['client']['packages'].each do |pg_pack| + + package pg_pack + +end diff --git a/chef/cookbooks/postgresql/recipes/config_initdb.rb b/chef/cookbooks/postgresql/recipes/config_initdb.rb new file mode 100644 index 0000000..92973ef --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/config_initdb.rb @@ -0,0 +1,148 @@ +# +# Cookbook Name:: postgresql +# Recipe:: config_initdb +# Author:: David Crane () +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +####### +# Load the locale_date_order() and select_default_timezone(tzdir) +# methods from libraries/default.rb +::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers) + +####### +# This recipe is derived from the setup_config() source code in the +# PostgreSQL initdb utility. It determines postgresql.conf settings that +# conform to the system's locale and timezone configuration, and also +# sets the error reporting and logging settings. +# +# See http://doxygen.postgresql.org/initdb_8c_source.html for the +# original initdb source code. +# +# By examining the system configuration, this recipe will set the +# following node.default['postgresql']['config'] attributes: +# +# - Locale and Formatting - +# * datestyle +# * lc_messages +# * lc_monetary +# * lc_numeric +# * lc_time +# * default_text_search_config +# +# - Timezone Conversion - +# * log_timezone +# * timezone +# +# In addition, this recipe will recommend the same error reporting and +# logging settings that initdb provided. These settings do differ from +# the PostgreSQL default settings, which would log to stderr only. The +# initdb settings rotate 7 days of log files named postgresql-Mon.log, +# etc. through these node.default['postgresql']['config'] attributes: +# +# - Where to Log - +# * log_destination = 'stderr' +# * log_directory = 'pg_log' +# * log_filename = 'postgresql-%a.log' +# (Default was: postgresql-%Y-%m-%d_%H%M%S.log) +# * logging_collector = true # on +# (Turned on to capture stderr logging and redirect into log files) +# (Default was: false # off) +# * log_rotation_age = 1d +# * log_rotation_size = 0 +# (Default was: 10MB) +# * log_truncate_on_rotation = true # on +# (Default was: false # off) + +####### +# Locale Configuration + +# See libraries/default.rb for the locale_date_order() method. +node.default['postgresql']['config']['datestyle'] = "iso, #{locale_date_order()}" + +# According to the locale(1) manpage, the locale settings are determined +# by environment variables according to the following precedence: +# LC_ALL > (LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME) > LANG. + +node.default['postgresql']['config']['lc_messages'] = + [ ENV['LC_ALL'], ENV['LC_MESSAGES'], ENV['LANG'] ].compact.first + +node.default['postgresql']['config']['lc_monetary'] = + [ ENV['LC_ALL'], ENV['LC_MONETARY'], ENV['LANG'] ].compact.first + +node.default['postgresql']['config']['lc_numeric'] = + [ ENV['LC_ALL'], ENV['LC_NUMERIC'], ENV['LANG'] ].compact.first + +node.default['postgresql']['config']['lc_time'] = + [ ENV['LC_ALL'], ENV['LC_TIME'], ENV['LANG'] ].compact.first + +node.default['postgresql']['config']['default_text_search_config'] = + case ENV['LANG'] + when /da_.*/ + 'pg_catalog.danish' + when /nl_.*/ + 'pg_catalog.dutch' + when /en_.*/ + 'pg_catalog.english' + when /fi_.*/ + 'pg_catalog.finnish' + when /fr_.*/ + 'pg_catalog.french' + when /de_.*/ + 'pg_catalog.german' + when /hu_.*/ + 'pg_catalog.hungarian' + when /it_.*/ + 'pg_catalog.italian' + when /no_.*/ + 'pg_catalog.norwegian' + when /pt_.*/ + 'pg_catalog.portuguese' + when /ro_.*/ + 'pg_catalog.romanian' + when /ru_.*/ + 'pg_catalog.russian' + when /es_.*/ + 'pg_catalog.spanish' + when /sv_.*/ + 'pg_catalog.swedish' + when /tr_.*/ + 'pg_catalog.turkish' + else + nil + end + +####### +# Timezone Configuration + +# Determine the name of the system's default timezone and specify node +# defaults for the postgresql.cof settings. If the timezone cannot be +# identified, do as initdb would do: leave it unspecified so PostgreSQL +# uses it's internal default of GMT. +tzdirpath = pg_TZDIR() # See libraries/default.rb +default_timezone = select_default_timezone(tzdirpath) # See libraries/default.rb +if !default_timezone.nil? + node.default['postgresql']['config']['log_timezone'] = default_timezone + node.default['postgresql']['config']['timezone'] = default_timezone +end + +####### +# - Where to Log - +node.default['postgresql']['config']['log_destination'] = 'stderr' +node.default['postgresql']['config']['log_directory'] = 'pg_log' +node.default['postgresql']['config']['log_filename'] = 'postgresql-%a.log' +node.default['postgresql']['config']['logging_collector'] = true # on +node.default['postgresql']['config']['log_rotation_age'] = '1d' +node.default['postgresql']['config']['log_rotation_size'] = 0 +node.default['postgresql']['config']['log_truncate_on_rotation'] = true # on diff --git a/chef/cookbooks/postgresql/recipes/config_pgtune.rb b/chef/cookbooks/postgresql/recipes/config_pgtune.rb new file mode 100644 index 0000000..ca0dc65 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/config_pgtune.rb @@ -0,0 +1,280 @@ +# +# Cookbook Name:: postgresql +# Recipe:: config_pgtune +# Author:: David Crane () +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +####### +# Load the binaryround(value) method from libraries/default.rb +::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers) + +####### +# This recipe is based on Greg Smith's pgtune script (the Feb 1, 2012 +# version at https://github.com/gregs1104/pgtune). Introduction: pgtune +# takes the wimpy default postgresql.conf and expands the database +# server to be as powerful as the hardware it's being deployed on. +# +# The default postgresql.conf aims at a system with approximately 128MB +# of RAM. This recipe recommends a baseline configuration in the right +# general range for a dedicated Postgresql system. +# +# This recipe takes three optional parameters that may be passed in as +# node['postgresql']['config_pgtune'] attributes: +# * db_type -- Specifies database type as one of: dw, oltp, +# web, mixed, desktop. If not specified, the default is mixed. +# * max_connections -- Specifies number of maximum connections +# expected. If not specified, it depends on database type. +# * total_memory -- Specifies total system memory. If not specified, +# it will be detected from the Ohai automatic attributes. +# +# Using those inputs, this recipe will compute and set the following +# node.default['postgresql']['config'] attributes: +# * max_connections +# * shared_buffers +# * effective_cache_size +# * work_mem +# * maintenance_work_mem +# * checkpoint_segments +# * checkpoint_completion_target +# * wal_buffers +# * default_statistics_target +# +# This recipe deviates from the original pgtune script for 2 settings: +# shared_buffers is capped for large memory systems (which Greg +# mentioned in a TODO.rst) and wal_buffers will auto-tune starting with +# 9.1 (which is a feature that Greg built into Postgresql). + +####### +# These are the workload characteristics of the five database types +# that can be specified as node['postgresql']['config_pgtune']['db_type']: +# +# dw -- Data Warehouse +# * Typically I/O- or RAM-bound +# * Large bulk loads of data +# * Large complex reporting queries +# * Also called "Decision Support" or "Business Intelligence" +# +# oltp -- Online Transaction Processing +# * Typically CPU- or I/O-bound +# * DB slightly larger than RAM to 1TB +# * 20-40% small data write queries +# * Some long transactions and complex read queries +# +# web -- Web Application +# * Typically CPU-bound +# * DB much smaller than RAM +# * 90% or more simple queries +# +# mixed -- Mixed DW and OLTP characteristics +# * A wide mixture of queries +# +# desktop -- Not a dedicated database +# * A general workstation, perhaps for a developer + +# Parse out db_type option, or use default. +db_type = 'mixed' + +if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('db_type')) + db_type = node['postgresql']['config_pgtune']['db_type'] + if (!(["dw","oltp","web","mixed","desktop"].include?(db_type))) + Chef::Application.fatal!([ + "Bad value (#{db_type})", + "for node['postgresql']['config_pgtune']['db_type'] attribute.", + "Valid values are one of dw, oltp, web, mixed, desktop." + ].join(' ')) + end +end + +# Parse out max_connections option, or use a value based on db_type. +con = +{ "web" => 200, + "oltp" => 300, + "dw" => 20, + "mixed" => 80, + "desktop" => 5 +}.fetch(db_type) + +if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('max_connections')) + max_connections = node['postgresql']['config_pgtune']['max_connections'] + if (max_connections.match(/\A[1-9]\d*\Z/) == nil) + Chef::Application.fatal!([ + "Bad value (#{max_connections})", + "for node['postgresql']['config_pgtune']['max_connections'] attribute.", + "Valid values are non-zero integers only." + ].join(' ')) + end + con = max_connections.to_i +end + +# Parse out total_memory option, or use value detected by Ohai. +total_memory = node['memory']['total'] + +# Override max_connections with a node attribute if DevOps desires. +# For example, on a system *not* dedicated to Postgresql. +if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('total_memory')) + total_memory = node['postgresql']['config_pgtune']['total_memory'] + if (total_memory.match(/\A[1-9]\d*kB\Z/) == nil) + Chef::Application.fatal!([ + "Bad value (#{total_memory})", + "for node['postgresql']['config_pgtune']['total_memory'] attribute.", + "Valid values are non-zero integers followed by kB (e.g., 49416564kB)." + ].join(' ')) + end +end + +# Ohai reports node[:memory][:total] in kB, as in "921756kB" +mem = total_memory.split("kB")[0].to_i / 1024 # in MB + +####### +# RAM-related settings computed as in Greg Smith's pgtune script. +# Remember that con and mem were either chosen above based on the +# db_type or the actual total memory, or were passed in attributes. + +# (1) max_connections +# Sets the maximum number of concurrent connections. +node.default['postgresql']['config']['max_connections'] = con + +# The calculations for the next four settings would not be optimal +# for low memory systems. In that case, the calculation is skipped, +# leaving the built-in Postgresql settings, which are actually +# intended for those low memory systems. +if (mem >= 256) + + # (2) shared_buffers + # Sets the number of shared memory buffers used by the server. + shared_buffers = + { "web" => mem/4, + "oltp" => mem/4, + "dw" => mem/4, + "mixed" => mem/4, + "desktop" => mem/16 + }.fetch(db_type) + + # Robert Haas has advised to cap the size of shared_buffers based on + # the memory architecture: 2GB on 32-bit and 8GB on 64-bit machines. + # http://rhaas.blogspot.com/2012/03/tuning-sharedbuffers-and-walbuffers.html + case node['kernel']['machine'] + when "i386" # 32-bit machines + if shared_buffers > 2*1024 + shared_buffers = 2*1024 + end + when "x86_64" # 64-bit machines + if shared_buffers > 8*1024 + shared_buffers = 8*1024 + end + end + + node.default['postgresql']['config']['shared_buffers'] = binaryround(shared_buffers*1024*1024) + + # (3) effective_cache_size + # Sets the planner's assumption about the size of the disk cache. + # That is, the portion of the kernel's disk cache that will be + # used for PostgreSQL data files. + effective_cache_size = + { "web" => mem * 3 / 4, + "oltp" => mem * 3 / 4, + "dw" => mem * 3 / 4, + "mixed" => mem * 3 / 4, + "desktop" => mem / 4 + }.fetch(db_type) + + node.default['postgresql']['config']['effective_cache_size'] = binaryround(effective_cache_size*1024*1024) + + # (4) work_mem + # Sets the maximum memory to be used for query workspaces. + work_mem = + { "web" => mem / con, + "oltp" => mem / con, + "dw" => mem / con / 2, + "mixed" => mem / con / 2, + "desktop" => mem / con / 6 + }.fetch(db_type) + + node.default['postgresql']['config']['work_mem'] = binaryround(work_mem*1024*1024) + + # (5) maintenance_work_mem + # Sets the maximum memory to be used for maintenance operations. + # This includes operations such as VACUUM and CREATE INDEX. + maintenance_work_mem = + { "web" => mem / 16, + "oltp" => mem / 16, + "dw" => mem / 8, + "mixed" => mem / 16, + "desktop" => mem / 16 + }.fetch(db_type) + + # Cap maintenence RAM at 1GB on servers with lots of memory + if (maintenance_work_mem > 1*1024) + maintenance_work_mem = 1*1024 + end + + node.default['postgresql']['config']['maintenance_work_mem'] = binaryround(maintenance_work_mem*1024*1024) + +end + +####### +# Checkpoint-related parameters that affect transaction rate and +# maximum tolerable recovery playback time. + +# (6) checkpoint_segments +# Sets the maximum distance in log segments between automatic WAL checkpoints. +checkpoint_segments = +{ "web" => 8, + "oltp" => 16, + "dw" => 64, + "mixed" => 16, + "desktop" => 3 +}.fetch(db_type) + +node.default['postgresql']['config']['checkpoint_segments'] = checkpoint_segments + +# (7) checkpoint_completion_target +# Time spent flushing dirty buffers during checkpoint, as fraction +# of checkpoint interval. +checkpoint_completion_target = +{ "web" => "0.7", + "oltp" => "0.9", + "dw" => "0.9", + "mixed" => "0.9", + "desktop" => "0.5" +}.fetch(db_type) + +node.default['postgresql']['config']['checkpoint_completion_target'] = checkpoint_completion_target + +# (8) wal_buffers +# Sets the number of disk-page buffers in shared memory for WAL. +# Starting with 9.1, wal_buffers will auto-tune if set to the -1 default. +# For 8.X and 9.0, it needed to be specified, which pgtune did as follows. +if node['postgresql']['version'].to_f < 9.1 + wal_buffers = 512 * checkpoint_segments + # The pgtune seems to use 1kB units for wal_buffers + node.default['postgresql']['config']['wal_buffers'] = binaryround(wal_buffers*1024) +else + node.default['postgresql']['config']['wal_buffers'] = "-1" +end + +# (9) default_statistics_target +# Sets the default statistics target. This applies to table columns +# that have not had a column-specific target set via +# ALTER TABLE SET STATISTICS. +default_statistics_target = +{ "web" => 100, + "oltp" => 100, + "dw" => 500, + "mixed" => 100, + "desktop" => 100 +}.fetch(db_type) + +node.default['postgresql']['config']['default_statistics_target'] = default_statistics_target diff --git a/chef/cookbooks/postgresql/recipes/contrib.rb b/chef/cookbooks/postgresql/recipes/contrib.rb new file mode 100644 index 0000000..5c43bea --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/contrib.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: postgresql +# Recipe:: contrib +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::server" + +# Install the PostgreSQL contrib package(s) from the distribution, +# as specified by the node attributes. +node['postgresql']['contrib']['packages'].each do |pg_pack| + + package pg_pack + +end + +# Install PostgreSQL contrib extentions into the template1 database, +# as specified by the node attributes. +if (node['postgresql']['contrib'].attribute?('extensions')) + node['postgresql']['contrib']['extensions'].each do |pg_ext| + bash "install-#{pg_ext}-extension" do + user 'postgres' + code <<-EOH + echo 'CREATE EXTENSION IF NOT EXISTS "#{pg_ext}";' | psql -d template1 + EOH + action :run + ::Chef::Resource.send(:include, Opscode::PostgresqlHelpers) + not_if {extension_installed?(pg_ext)} + end + end +end diff --git a/chef/cookbooks/postgresql/recipes/default.rb b/chef/cookbooks/postgresql/recipes/default.rb new file mode 100644 index 0000000..785b168 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: postgresql +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::client" diff --git a/chef/cookbooks/postgresql/recipes/ruby.rb b/chef/cookbooks/postgresql/recipes/ruby.rb new file mode 100644 index 0000000..9a9e4f7 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/ruby.rb @@ -0,0 +1,118 @@ +# +# Cookbook Name:: postgresql +# Recipe:: ruby +# +# Author:: Joshua Timberman () +# Copyright 2012 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Load the pgdgrepo_rpm_info method from libraries/default.rb +::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers) + +begin + require 'pg' +rescue LoadError + execute "apt-get update" do + ignore_failure true + action :nothing + end.run_action(:run) if node['platform_family'] == "debian" + + node.set['build_essential']['compiletime'] = true + include_recipe "build-essential" + include_recipe "postgresql::client" + + if node['postgresql']['enable_pgdg_yum'] + repo_rpm_url, repo_rpm_filename, repo_rpm_package = pgdgrepo_rpm_info + include_recipe "postgresql::yum_pgdg_postgresql" + resources("remote_file[#{Chef::Config[:file_cache_path]}/#{repo_rpm_filename}]").run_action(:create) + resources("package[#{repo_rpm_package}]").run_action(:install) + ENV['PATH'] = "/usr/pgsql-#{node['postgresql']['version']}/bin:#{ENV['PATH']}" + end + + if node['postgresql']['enable_pgdg_apt'] + include_recipe "postgresql::apt_pgdg_postgresql" + resources("file[remove deprecated Pitti PPA apt repository]").run_action(:delete) + resources("apt_repository[apt.postgresql.org]").run_action(:add) + end + + node['postgresql']['client']['packages'].each do |pg_pack| + + resources("package[#{pg_pack}]").run_action(:install) + + end + + begin + chef_gem "pg" + rescue Gem::Installer::ExtensionBuildError => e + # Are we an omnibus install? + raise if RbConfig.ruby.scan(%r{(chef|opscode)}).empty? + # Still here, must be omnibus. Lets make this thing install! + Chef::Log.warn 'Failed to properly build pg gem. Forcing properly linking and retrying (omnibus fix)' + gem_dir = e.message.scan(%r{will remain installed in ([^ ]+)}).flatten.first + raise unless gem_dir + gem_name = File.basename(gem_dir) + ext_dir = File.join(gem_dir, 'ext') + gem_exec = File.join(File.dirname(RbConfig.ruby), 'gem') + new_content = <<-EOS +require 'rbconfig' +%w( +configure_args +LIBRUBYARG_SHARED +LIBRUBYARG_STATIC +LIBRUBYARG +LDFLAGS +).each do |key| + RbConfig::CONFIG[key].gsub!(/-Wl[^ ]+( ?\\/[^ ]+)?/, '') + RbConfig::MAKEFILE_CONFIG[key].gsub!(/-Wl[^ ]+( ?\\/[^ ]+)?/, '') +end +RbConfig::CONFIG['RPATHFLAG'] = '' +RbConfig::MAKEFILE_CONFIG['RPATHFLAG'] = '' +EOS + new_content << File.read(extconf_path = File.join(ext_dir, 'extconf.rb')) + File.open(extconf_path, 'w') do |file| + file.write(new_content) + end + + lib_builder = execute 'generate pg gem Makefile' do + command "#{RbConfig.ruby} extconf.rb" + cwd ext_dir + action :nothing + end + lib_builder.run_action(:run) + + lib_maker = execute 'make pg gem lib' do + command 'make' + cwd ext_dir + action :nothing + end + lib_maker.run_action(:run) + + lib_installer = execute 'install pg gem lib' do + command 'make install' + cwd ext_dir + action :nothing + end + lib_installer.run_action(:run) + + spec_installer = execute 'install pg spec' do + command "#{gem_exec} spec ./cache/#{gem_name}.gem --ruby > ./specifications/#{gem_name}.gemspec" + cwd File.join(gem_dir, '..', '..') + action :nothing + end + spec_installer.run_action(:run) + + Chef::Log.warn 'Installation of pg gem successful!' + end +end diff --git a/chef/cookbooks/postgresql/recipes/server.rb b/chef/cookbooks/postgresql/recipes/server.rb new file mode 100644 index 0000000..933fced --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/server.rb @@ -0,0 +1,90 @@ +# +# Cookbook Name:: postgresql +# Recipe:: server +# +# Author:: Joshua Timberman () +# Author:: Lamont Granquist () +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) + +include_recipe "postgresql::client" + +# randomly generate postgres password, unless using solo - see README +if Chef::Config[:solo] + missing_attrs = %w{ + postgres + }.select do |attr| + node['postgresql']['password'][attr].nil? + end.map { |attr| "node['postgresql']['password']['#{attr}']" } + + if !missing_attrs.empty? + Chef::Application.fatal!([ + "You must set #{missing_attrs.join(', ')} in chef-solo mode.", + "For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note" + ].join(' ')) + end +else + # TODO: The "secure_password" is randomly generated plain text, so it + # should be converted to a PostgreSQL specific "encrypted password" if + # it should actually install a password (as opposed to disable password + # login for user 'postgres'). However, a random password wouldn't be + # useful if it weren't saved as clear text in Chef Server for later + # retrieval. + node.set_unless['postgresql']['password']['postgres'] = secure_password + node.save +end + +# Include the right "family" recipe for installing the server +# since they do things slightly differently. +case node['platform_family'] +when "rhel", "fedora", "suse" + include_recipe "postgresql::server_redhat" +when "debian" + include_recipe "postgresql::server_debian" +end + +template "#{node['postgresql']['dir']}/postgresql.conf" do + source "postgresql.conf.erb" + owner "postgres" + group "postgres" + mode 0600 + notifies :reload, 'service[postgresql]', :immediately +end + +template "#{node['postgresql']['dir']}/pg_hba.conf" do + source "pg_hba.conf.erb" + owner "postgres" + group "postgres" + mode 00600 + notifies :reload, 'service[postgresql]', :immediately +end + +# NOTE: Consider two facts before modifying "assign-postgres-password": +# (1) Passing the "ALTER ROLE ..." through the psql command only works +# if passwordless authorization was configured for local connections. +# For example, if pg_hba.conf has a "local all postgres ident" rule. +# (2) It is probably fruitless to optimize this with a not_if to avoid +# setting the same password. This chef recipe doesn't have access to +# the plain text password, and testing the encrypted (md5 digest) +# version is not straight-forward. +bash "assign-postgres-password" do + user 'postgres' + code <<-EOH +echo "ALTER ROLE postgres ENCRYPTED PASSWORD '#{node['postgresql']['password']['postgres']}';" | psql + EOH + action :run +end diff --git a/chef/cookbooks/postgresql/recipes/server_debian.rb b/chef/cookbooks/postgresql/recipes/server_debian.rb new file mode 100644 index 0000000..7345f4e --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/server_debian.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: postgresql +# Recipe:: server +# +# Author:: Joshua Timberman () +# Author:: Lamont Granquist ()# +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::client" + +node['postgresql']['server']['packages'].each do |pg_pack| + + package pg_pack + +end + +service "postgresql" do + service_name node['postgresql']['server']['service_name'] + supports :restart => true, :status => true, :reload => true + action [:enable, :start] +end diff --git a/chef/cookbooks/postgresql/recipes/server_redhat.rb b/chef/cookbooks/postgresql/recipes/server_redhat.rb new file mode 100644 index 0000000..81ebe39 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/server_redhat.rb @@ -0,0 +1,72 @@ +# +# Cookbook Name:: postgresql +# Recipe:: server +# +# Author:: Joshua Timberman () +# Author:: Lamont Granquist () +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::client" + +# Create a group and user like the package will. +# Otherwise the templates fail. + +group "postgres" do + gid 26 +end + +user "postgres" do + shell "/bin/bash" + comment "PostgreSQL Server" + home "/var/lib/pgsql" + gid "postgres" + system true + uid 26 + supports :manage_home => false +end + +directory node['postgresql']['dir'] do + owner "postgres" + group "postgres" + recursive true + action :create +end + +node['postgresql']['server']['packages'].each do |pg_pack| + + package pg_pack + +end + +template "/etc/sysconfig/pgsql/#{node['postgresql']['server']['service_name']}" do + source "pgsql.sysconfig.erb" + mode "0644" + notifies :restart, "service[postgresql]", :delayed +end + +unless platform_family?("suse") + + execute "/sbin/service #{node['postgresql']['server']['service_name']} initdb" do + not_if { ::FileTest.exist?(File.join(node['postgresql']['dir'], "PG_VERSION")) } + end + +end + +service "postgresql" do + service_name node['postgresql']['server']['service_name'] + supports :restart => true, :status => true, :reload => true + action [:enable, :start] +end diff --git a/chef/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb b/chef/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb new file mode 100644 index 0000000..f05ad35 --- /dev/null +++ b/chef/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb @@ -0,0 +1,47 @@ +# +# Cookbook Name:: postgresql +# Recipe::yum_pgdg_postgresql +# +# Copyright 2013, DonorsChoose.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +####### +# Load the pgdgrepo_rpm_info method from libraries/default.rb +::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers) + +###################################### +# Install the "PostgreSQL RPM Building Project - Yum Repository" through +# the repo_rpm_url determined with pgdgrepo_rpm_info method from +# libraries/default.rb. The /etc/yum.repos.d/pgdg-*.repo +# will provide postgresql9X packages, but you may need to exclude +# postgresql packages from the repository of the distro in order to use +# PGDG repository properly. Conflicts will arise if postgresql9X does +# appear in your distro's repo and you want a more recent patch level. + +repo_rpm_url, repo_rpm_filename, repo_rpm_package = pgdgrepo_rpm_info + +# Download the PGDG repository RPM as a local file +remote_file "#{Chef::Config[:file_cache_path]}/#{repo_rpm_filename}" do + source repo_rpm_url + mode "0644" +end + +# Install the PGDG repository RPM from the local file +# E.g., /etc/yum.repos.d/pgdg-91-centos.repo +package repo_rpm_package do + provider Chef::Provider::Package::Rpm + source "#{Chef::Config[:file_cache_path]}/#{repo_rpm_filename}" + action :install +end diff --git a/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb b/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb new file mode 100644 index 0000000..8db8ee8 --- /dev/null +++ b/chef/cookbooks/postgresql/templates/default/pg_hba.conf.erb @@ -0,0 +1,35 @@ +# This file was automatically generated and dropped off by Chef! + +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the PostgreSQL +# documentation for a complete description of this file. + +<% if node['postgresql']['version'].to_f < 9.1 -%> +# TYPE DATABASE USER CIDR-ADDRESS METHOD +<% elsif node['postgresql']['version'].to_f >= 9.1 -%> +# TYPE DATABASE USER ADDRESS METHOD +<% end -%> + +# "local" is for Unix domain socket connections only +<% if node['postgresql']['version'].to_f < 9.1 -%> +local all all ident +<% elsif node['postgresql']['version'].to_f >= 9.1 -%> +local all all peer +<% end -%> + +########### +# Other authentication configurations taken from chef node defaults: +########### +<% node['postgresql']['pg_hba'].each do |auth| -%> + +<% if auth[:comment] %> +<%= auth[:comment] %> +<% end %> +<% if auth[:addr] %> +<%= auth[:type].ljust(7) %> <%= auth[:db].ljust(15) %> <%= auth[:user].ljust(15) %> <%= auth[:addr].ljust(23) %> <%= auth[:method] %> +<% else %> +<%= auth[:type].ljust(7) %> <%= auth[:db].ljust(15) %> <%= auth[:user].ljust(15) %> <%= auth[:method] %> +<% end %> +<% end %> diff --git a/chef/cookbooks/postgresql/templates/default/pgsql.sysconfig.erb b/chef/cookbooks/postgresql/templates/default/pgsql.sysconfig.erb new file mode 100644 index 0000000..5421211 --- /dev/null +++ b/chef/cookbooks/postgresql/templates/default/pgsql.sysconfig.erb @@ -0,0 +1,4 @@ +PGDATA=<%= node['postgresql']['dir'] %> +<% if node['postgresql']['config'].attribute?("port") -%> +PGPORT=<%= node['postgresql']['config']['port'] %> +<% end -%> diff --git a/chef/cookbooks/postgresql/templates/default/postgresql.conf.erb b/chef/cookbooks/postgresql/templates/default/postgresql.conf.erb new file mode 100644 index 0000000..d2facc2 --- /dev/null +++ b/chef/cookbooks/postgresql/templates/default/postgresql.conf.erb @@ -0,0 +1,20 @@ +# PostgreSQL configuration file +# This file was automatically generated and dropped off by chef! +# Please refer to the PostgreSQL documentation for details on +# configuration settings. + +<% node['postgresql']['config'].sort.each do |key, value| %> +<% next if value.nil? -%> +<%= key %> = <%= + case value + when String + "'#{value}'" + when TrueClass + 'on' + when FalseClass + 'off' + else + value + end +%> +<% end %> diff --git a/chef/cookbooks/python/.gitignore b/chef/cookbooks/python/.gitignore new file mode 100644 index 0000000..c7eb8a2 --- /dev/null +++ b/chef/cookbooks/python/.gitignore @@ -0,0 +1,16 @@ +.vagrant +Berksfile.lock +*~ +*# +.#* +\#*# +.*.sw[a-z] +*.un~ +/cookbooks + +# Bundler +Gemfile.lock +bin/* +.bundle/* +.kitchen/ +.kitchen.local.yml diff --git a/chef/cookbooks/python/.kitchen.yml b/chef/cookbooks/python/.kitchen.yml new file mode 100644 index 0000000..7eaf82f --- /dev/null +++ b/chef/cookbooks/python/.kitchen.yml @@ -0,0 +1,57 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.10 + driver_config: + box: opscode-ubuntu-12.10 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.10_provisionerless.box + run_list: ["recipe[apt]"] + +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +suites: +- name: default + run_list: + - recipe[minitest-handler] + - recipe[python] + - recipe[python_test::cook-3084] + attributes: {} + +- name: source + run_list: + - recipe[minitest-handler] + - recipe[python] + - recipe[python_test::cook-3084] + attributes: {python: {install_method: "source"}} +- name: exert + excludes: ["centos-5.9"] + run_list: + - recipe[python] + - recipe[python_test::test_exert] +- name: virtualenv + run_list: + - recipe[python] + - recipe[python_test::test_virtualenv] diff --git a/chef/cookbooks/python/Berksfile b/chef/cookbooks/python/Berksfile new file mode 100644 index 0000000..f795efe --- /dev/null +++ b/chef/cookbooks/python/Berksfile @@ -0,0 +1,11 @@ +site :opscode + +metadata + +group :integration do + cookbook "minitest-handler" + cookbook "apt" + cookbook "yum" + cookbook "build-essential" + cookbook "python_test", :path => "./test/cookbooks/python_test" +end diff --git a/chef/cookbooks/python/CHANGELOG.md b/chef/cookbooks/python/CHANGELOG.md new file mode 100644 index 0000000..6d0e7b0 --- /dev/null +++ b/chef/cookbooks/python/CHANGELOG.md @@ -0,0 +1,84 @@ +python Cookbook CHANGELOG +========================= +This file is used to list changes made in each version of the python cookbook. + + +v1.4.0 +------ +### New Feature +- **[COOK-3248](https://tickets.opscode.com/browse/COOK-3248)** - Improve testing suite + +### Improvement +- **[COOK-3125](https://tickets.opscode.com/browse/COOK-3125)** - Don't use `normal` attributes + +### Bug +- **[COOK-3084](https://tickets.opscode.com/browse/COOK-3084)** - Fix `python_virtualenv` on EL 5 + +v1.3.6 +------ +### Bug +- [COOK-3305]: distribute merged back into setuptools + +### New Feature +- [COOK-3248]: Improve testing suite in the python cookbook + +v1.3.4 +------ +### Bug +- [COOK-3137]: `python_pip` LWRP cannot have differnent name and `package_name` + +v1.3.2 +------ +### Bug +- [COOK-2917]: python::source fails on CentOS 6.3 minimal (make: command not found) +- [COOK-3077]: Python - pip fails to install when `['python']['install_method'] == 'source'` + +v1.3.0 +------ +### Bug +- [COOK-2376]: Python pip default action +- [COOK-2468]: python cookbook - Chef 11 compat fixes +- [COOK-2882]: Python source recipe fails on Ubuntu 12.10 because of unavailable libdb4.8-dev package +- [COOK-3009]: fix build time dependencies and gcc flags for python source on newer ubuntus + +### New Feature +- [COOK-2449]: Make the distribute download location an attribute +- [COOK-3008]: Update python::source to install 2.7.5 + +### Sub-task +- [COOK-2866]: python::source checks existence of a directory that already exists + +v1.2.2 +------ +- [COOK-2297] - more gracefully handle pip packages from VCS and source archives + +v1.2.0 +------ +- [COOK-1866] - /usr/bin is not a pip binary location in source installs on RHEL +- [COOK-1925] - add smartos support + +v1.1.0 +------ +- [COOK-1715] - Add user and group to python_pip +- [COOK-1727] - Python cookbook cannot install `pip` on CentOS versions < 6 + +v1.0.8 +------ +- [COOK-1016] - python package needs separate names for centos/rhel 5.x vs 6.x +- [COOK-1048] - installation of pip does not honor selected python version +- [COOK-1282] - catch Chef::Exceptions::ShellCommandFailed for chef 0.10.8 compatibility +- [COOK-1311] - virtualenv should have options attribute +- [COOK-1320] - pip provider doesn't catch correct exception +- [COOK-1415] - use plain 'python' binary instead of versioned one for default interpreter + +v1.0.6 +------ +- [COOK-1036] - correctly grep for python-module version +- [COOK-1046] - run pip inside the virtualenv + +v1.0.4 +------ +- [COOK-960] - add timeout to python_pip +- [COOK-651] - 'install_path' not correctly resolved when using python::source +- [COOK-650] - Add ability to specify version when installing distribute. +- [COOK-553] - FreeBSD support in the python cookbook diff --git a/chef/cookbooks/python/CONTRIBUTING b/chef/cookbooks/python/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/python/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/python/Gemfile b/chef/cookbooks/python/Gemfile new file mode 100644 index 0000000..2fea7a8 --- /dev/null +++ b/chef/cookbooks/python/Gemfile @@ -0,0 +1,15 @@ +source 'https://rubygems.org' + +gem 'rake' +gem 'rspec' +gem 'foodcritic' +gem 'berkshelf' +gem 'thor-foodcritic' +gem 'vagrant-wrapper' + +group :integration do + gem 'test-kitchen', :git => "git://github.com/opscode/test-kitchen.git" + gem 'kitchen-vagrant', :git => "git://github.com/opscode/kitchen-vagrant.git" + gem 'kitchen-ec2', :git => "git://github.com/opscode/kitchen-ec2.git" + gem 'kitchen-lxc', :git => "https://github.com/portertech/kitchen-lxc.git", :tag => 'v0.0.1.beta2' +end diff --git a/chef/cookbooks/python/LICENSE b/chef/cookbooks/python/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/python/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/python/README.md b/chef/cookbooks/python/README.md new file mode 100644 index 0000000..30af70e --- /dev/null +++ b/chef/cookbooks/python/README.md @@ -0,0 +1,163 @@ +python Cookbook +=============== +Installs and configures Python. Also includes LWRPs for managing python packages with `pip` and `virtualenv` isolated Python environments. + + +Requirements +------------ +### Platforms +- Debian, Ubuntu +- CentOS, Red Hat, Fedora + +### Cookbooks +- build-essential +- yum + +NOTE: The `yum` cookbook is a dependency of the cookbook, and will be used to install [EPEL](http://fedoraproject.org/wiki/EPEL) on RedHet/CentOS 5.x systems to provide the Python 2.6 packages. + + +Attributes +---------- +See `attributes/default.rb` for default values. + +- `node["python"]["install_method"]` - method to install python with, default `package`. + +The file also contains the following attributes: + +- platform specific locations and settings +- source installation settings + + +Resource/Provider +----------------- +This cookbook includes LWRPs for managing: + +- pip packages +- virtualenv isolated Python environments + +### `python_pip` +Install packages using the new hotness in Python package management...[`pip`](http://pypi.python.org/pypi/pip). Yo dawg...easy_install is so 2009, you better ask your local Pythonista if you don't know! The usage semantics are like that of any normal package provider. + +#### Actions +- :install: Install a pip package - if version is provided, install that specific version (default) +- :upgrade: Upgrade a pip package - if version is provided, upgrade to that specific version +- :remove: Remove a pip package +- :user: User to run pip as, for using with virtualenv +- :group: Group to run pip as, for using with virtualenv +- :purge: Purge a pip package (this usually entails removing configuration files as well as the package itself). With pip packages this behaves the same as `:remove` + +#### Attribute Parameters +- package_name: name attribute. The name of the pip package to install +- version: the version of the package to install/upgrade. If no version is given latest is assumed. +- virtualenv: virtualenv environment to install pip package into +- options: Add additional options to the underlying pip package command +- timeout: timeout in seconds for the command to execute. Useful for pip packages that may take a long time to install. Default 900 seconds. + +#### Examples +```ruby +# install latest gunicorn into system path +python_pip "gunicorn" + +# target a virtualenv +python_pip "gunicorn" do + virtualenv "/home/ubunut/my_ve" +end +``` + +```ruby +# install Django 1.1.4 +python_pip "django" do + version "1.1.4" +end +``` + +```ruby +# use this provider with the core package resource +package "django" do + provider Chef::Provider::PythonPip +end +``` + +### `python_virtualenv` +[`virtualenv`](http://pypi.python.org/pypi/virtualenv) is a great tool that creates isolated python environments. Think of it as RVM without all those hipsters and tight jeans. + +#### Actions +- :create: creates a new virtualenv +- :delete: deletes an existing virtualenv + +#### Attribute Parameters +- path: name attribute. The path where the virtualenv will be created +- interpreter: The Python interpreter to use. default is null (i.e. use whatever python the virtualenv command is using). +- owner: The owner for the virtualenv +- group: The group owner of the file (string or id) +- options : Command line options (string) + +#### Examples +```ruby +# create a 2.6 virtualenv owned by ubuntu user +python_virtualenv "/home/ubuntu/my_cool_ve" do + owner "ubuntu" + group "ubuntu" + action :create +end +``` + +```ruby +# create a Python 2.4 virtualenv +python_virtualenv "/home/ubuntu/my_old_ve" do + interpreter "python2.4" + owner "ubuntu" + group "ubuntu" + action :create +end +``` + +```ruby +# create a Python 2.6 virtualenv with access to the global packages owned by ubuntu user +python_virtualenv "/home/ubuntu/my_old_ve" do + owner "ubuntu" + group "ubuntu" + options "--system-site-packages" + action :create +end +``` + + +Usage +----- +### default +Include default recipe in a run list, to get `python`, `pip` and `virtualenv`. Installs python by package or source depending on the platform. + +### package +Installs Python from packages. + +### source +Installs Python from source. + +### pip +Installs `pip` from source. + +### virtualenv + +Installs virtualenv using the `python_pip` resource. + + +License & Authors +----------------- +- Author:: Seth Chisamore () + +```text +Copyright:: 2011, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/python/TESTING.md b/chef/cookbooks/python/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/python/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/python/attributes/default.rb b/chef/cookbooks/python/attributes/default.rb new file mode 100644 index 0000000..aeeaa77 --- /dev/null +++ b/chef/cookbooks/python/attributes/default.rb @@ -0,0 +1,42 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: python +# Attribute:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['python']['install_method'] = 'package' + +if python['install_method'] == 'package' + case platform + when "smartos" + default['python']['prefix_dir'] = '/opt/local' + else + default['python']['prefix_dir'] = '/usr' + end +else + default['python']['prefix_dir'] = '/usr/local' +end + +default['python']['binary'] = "#{python['prefix_dir']}/bin/python" + +default['python']['url'] = 'http://www.python.org/ftp/python' +default['python']['version'] = '2.7.5' +default['python']['checksum'] = '3b477554864e616a041ee4d7cef9849751770bc7c39adaf78a94ea145c488059' +default['python']['configure_options'] = %W{--prefix=#{python['prefix_dir']}} + +default['python']['setuptools_script_url'] = 'https://bitbucket.org/pypa/setuptools/raw/0.8/ez_setup.py' +default['python']['pip_script_url'] = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py' diff --git a/chef/cookbooks/python/metadata.rb b/chef/cookbooks/python/metadata.rb new file mode 100644 index 0000000..8128d6d --- /dev/null +++ b/chef/cookbooks/python/metadata.rb @@ -0,0 +1,19 @@ +name "python" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs Python, pip and virtualenv. Includes LWRPs for managing Python packages with `pip` and `virtualenv` isolated Python environments." +version "1.4.1" + +depends "build-essential" +depends "yum" + +recipe "python", "Installs python, pip, and virtualenv" +recipe "python::package", "Installs python using packages." +recipe "python::source", "Installs python from source." +recipe "python::pip", "Installs pip from source." +recipe "python::virtualenv", "Installs virtualenv using the python_pip resource." + +%w{ debian ubuntu centos redhat fedora freebsd smartos }.each do |os| + supports os +end diff --git a/chef/cookbooks/python/providers/pip.rb b/chef/cookbooks/python/providers/pip.rb new file mode 100644 index 0000000..e7010d2 --- /dev/null +++ b/chef/cookbooks/python/providers/pip.rb @@ -0,0 +1,168 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Provider:: pip +# +# Copyright:: 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' +require 'chef/mixin/language' +include Chef::Mixin::ShellOut + +def whyrun_supported? + true +end + +# the logic in all action methods mirror that of +# the Chef::Provider::Package which will make +# refactoring into core chef easy + +action :install do + # If we specified a version, and it's not the current version, move to the specified version + if new_resource.version != nil && new_resource.version != current_resource.version + install_version = new_resource.version + # If it's not installed at all, install it + elsif current_resource.version == nil + install_version = candidate_version + end + + if install_version + description = "install package #{new_resource} version #{install_version}" + converge_by(description) do + Chef::Log.info("Installing #{new_resource} version #{install_version}") + status = install_package(install_version) + if status + new_resource.updated_by_last_action(true) + end + end + end +end + +action :upgrade do + if current_resource.version != candidate_version + orig_version = current_resource.version || "uninstalled" + description = "upgrade #{current_resource} version from #{current_resource.version} to #{candidate_version}" + converge_by(description) do + Chef::Log.info("Upgrading #{new_resource} version from #{orig_version} to #{candidate_version}") + status = upgrade_package(candidate_version) + if status + new_resource.updated_by_last_action(true) + end + end + end +end + +action :remove do + if removing_package? + description = "remove package #{new_resource}" + converge_by(description) do + Chef::Log.info("Removing #{new_resource}") + remove_package(new_resource.version) + new_resource.updated_by_last_action(true) + end + end +end + +def removing_package? + if current_resource.version.nil? + false # nothing to remove + elsif new_resource.version.nil? + true # remove any version of a package + elsif new_resource.version == current_resource.version + true # remove the version we have + else + false # we don't have the version we want to remove + end +end + +# these methods are the required overrides of +# a provider that extends from Chef::Provider::Package +# so refactoring into core Chef should be easy + +def load_current_resource + @current_resource = Chef::Resource::PythonPip.new(new_resource.name) + @current_resource.package_name(new_resource.package_name) + @current_resource.version(nil) + + unless current_installed_version.nil? + @current_resource.version(current_installed_version) + end + + @current_resource +end + +def current_installed_version + @current_installed_version ||= begin + delimeter = /==/ + + version_check_cmd = "#{which_pip(new_resource)} freeze | grep -i '^#{new_resource.package_name}=='" + # incase you upgrade pip with pip! + if new_resource.package_name.eql?('pip') + delimeter = /\s/ + version_check_cmd = "pip --version" + end + result = shell_out(version_check_cmd) + (result.exitstatus == 0) ? result.stdout.split(delimeter)[1].strip : nil + end +end + +def candidate_version + @candidate_version ||= begin + # `pip search` doesn't return versions yet + # `pip list` may be coming soon: + # https://bitbucket.org/ianb/pip/issue/197/option-to-show-what-version-would-be + new_resource.version||'latest' + end +end + +def install_package(version) + # if a version isn't specified (latest), is a source archive (ex. http://my.package.repo/SomePackage-1.0.4.zip), + # or from a VCS (ex. git+https://git.repo/some_pkg.git) then do not append a version as this will break the source link + if version == 'latest' || new_resource.package_name.downcase.start_with?('http:', 'https:') || ['git', 'hg', 'svn'].include?(new_resource.package_name.downcase.split('+')[0]) + version = '' + else + version = "==#{version}" + end + pip_cmd('install', version) +end + +def upgrade_package(version) + new_resource.options "#{new_resource.options} --upgrade" + install_package(version) +end + +def remove_package(version) + new_resource.options "#{new_resource.options} --yes" + pip_cmd('uninstall') +end + +def pip_cmd(subcommand, version='') + options = { :timeout => new_resource.timeout, :user => new_resource.user, :group => new_resource.group } + options[:environment] = { 'HOME' => ::File.expand_path("~#{new_resource.user}") } if new_resource.user + shell_out!("#{which_pip(new_resource)} #{subcommand} #{new_resource.options} #{new_resource.package_name}#{version}", options) +end + +# TODO remove when provider is moved into Chef core +# this allows PythonPip to work with Chef::Resource::Package +def which_pip(nr) + if (nr.respond_to?("virtualenv") && nr.virtualenv) + ::File.join(nr.virtualenv,'/bin/pip') + elsif node['python']['install_method'].eql?("source") + ::File.join(node['python']['prefix_dir'], "/bin/pip") + else + 'pip' + end +end diff --git a/chef/cookbooks/python/providers/virtualenv.rb b/chef/cookbooks/python/providers/virtualenv.rb new file mode 100644 index 0000000..addb724 --- /dev/null +++ b/chef/cookbooks/python/providers/virtualenv.rb @@ -0,0 +1,75 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Provider:: virtualenv +# +# Copyright:: 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' +require 'chef/mixin/language' +include Chef::Mixin::ShellOut + +def whyrun_supported? + true +end + +action :create do + unless exists? + Chef::Log.info("Creating virtualenv #{new_resource} at #{new_resource.path}") + interpreter = new_resource.interpreter ? " --python=#{new_resource.interpreter}" : "" + execute "#{virtualenv_cmd}#{interpreter} #{new_resource.options} #{new_resource.path}" do + user new_resource.owner if new_resource.owner + group new_resource.group if new_resource.group + end + new_resource.updated_by_last_action(true) + end +end + +action :delete do + if exists? + description = "delete virtualenv #{new_resource} at #{new_resource.path}" + converge_by(description) do + Chef::Log.info("Deleting virtualenv #{new_resource} at #{new_resource.path}") + FileUtils.rm_rf(new_resource.path) + end + end +end + +def load_current_resource + @current_resource = Chef::Resource::PythonVirtualenv.new(new_resource.name) + @current_resource.path(new_resource.path) + + if exists? + cstats = ::File.stat(current_resource.path) + @current_resource.owner(cstats.uid) + @current_resource.group(cstats.gid) + end + @current_resource +end + +def virtualenv_cmd() + if node['python']['install_method'].eql?("source") + ::File.join(node['python']['prefix_dir'], "/bin/virtualenv") + else + "virtualenv" + end +end + +private +def exists? + ::File.exist?(current_resource.path) && ::File.directory?(current_resource.path) \ + && ::File.exists?("#{current_resource.path}/bin/activate") +end diff --git a/chef/cookbooks/python/recipes/default.rb b/chef/cookbooks/python/recipes/default.rb new file mode 100644 index 0000000..ee9afe8 --- /dev/null +++ b/chef/cookbooks/python/recipes/default.rb @@ -0,0 +1,23 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Recipe:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "python::#{node['python']['install_method']}" +include_recipe "python::pip" +include_recipe "python::virtualenv" diff --git a/chef/cookbooks/python/recipes/package.rb b/chef/cookbooks/python/recipes/package.rb new file mode 100644 index 0000000..18c1a00 --- /dev/null +++ b/chef/cookbooks/python/recipes/package.rb @@ -0,0 +1,43 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Recipe:: package +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +major_version = node['platform_version'].split('.').first.to_i + +# COOK-1016 Handle RHEL/CentOS namings of python packages, by installing EPEL +# repo & package +if platform_family?('rhel') && major_version < 6 + include_recipe 'yum::epel' + python_pkgs = ["python26", "python26-devel"] + node.default['python']['binary'] = "/usr/bin/python26" +else + python_pkgs = value_for_platform_family( + "debian" => ["python","python-dev"], + "rhel" => ["python","python-devel"], + "freebsd" => ["python"], + "smartos" => ["python27"], + "default" => ["python","python-dev"] + ) +end + +python_pkgs.each do |pkg| + package pkg do + action :install + end +end diff --git a/chef/cookbooks/python/recipes/pip.rb b/chef/cookbooks/python/recipes/pip.rb new file mode 100644 index 0000000..65acfa7 --- /dev/null +++ b/chef/cookbooks/python/recipes/pip.rb @@ -0,0 +1,62 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Recipe:: pip +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Where does pip get installed? +# platform/method: path (proof) +# redhat/package: /usr/bin/pip (sha a8a3a3) +# omnibus/source: /opt/local/bin/pip (sha 29ce9874) + +if node['python']['install_method'] == 'source' + pip_binary = "#{node['python']['prefix_dir']}/bin/pip" +elsif platform_family?("rhel") + pip_binary = "/usr/bin/pip" +elsif platform_family?("smartos") + pip_binary = "/opt/local/bin/pip" +else + pip_binary = "/usr/local/bin/pip" +end + +remote_file "#{Chef::Config[:file_cache_path]}/ez_setup.py" do + source node['python']['setuptools_script_url'] + mode "0644" + not_if "#{node['python']['binary']} -c 'import setuptools'" +end + +remote_file "#{Chef::Config[:file_cache_path]}/get-pip.py" do + source node['python']['pip_script_url'] + mode "0644" + not_if { ::File.exists?(pip_binary) } +end + +execute "install-setuptools" do + cwd Chef::Config[:file_cache_path] + command <<-EOF + #{node['python']['binary']} ez_setup.py + EOF + not_if "#{node['python']['binary']} -c 'import setuptools'" +end + +execute "install-pip" do + cwd Chef::Config[:file_cache_path] + command <<-EOF + #{node['python']['binary']} get-pip.py + EOF + not_if { ::File.exists?(pip_binary) } +end diff --git a/chef/cookbooks/python/recipes/source.rb b/chef/cookbooks/python/recipes/source.rb new file mode 100644 index 0000000..eb8288d --- /dev/null +++ b/chef/cookbooks/python/recipes/source.rb @@ -0,0 +1,58 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Recipe:: source +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "build-essential" + +configure_options = node['python']['configure_options'].join(" ") + +packages = value_for_platform_family( + "rhel" => ["openssl-devel","bzip2-devel","zlib-devel","expat-devel","db4-devel","sqlite-devel","ncurses-devel","readline-devel"], + "default" => ["libssl-dev","libbz2-dev","zlib1g-dev","libexpat1-dev","libdb-dev","libsqlite3-dev","libncursesw5-dev","libncurses5-dev","libreadline-dev","libsasl2-dev", "libgdbm-dev"] + ) +# +packages.each do |dev_pkg| + package dev_pkg +end + +version = node['python']['version'] +install_path = "#{node['python']['prefix_dir']}/bin/python#{version.split(/(^\d+\.\d+)/)[1]}" + +remote_file "#{Chef::Config[:file_cache_path]}/Python-#{version}.tar.bz2" do + source "#{node['python']['url']}/#{version}/Python-#{version}.tar.bz2" + checksum node['python']['checksum'] + mode "0644" + not_if { ::File.exists?(install_path) } +end + +bash "build-and-install-python" do + cwd Chef::Config[:file_cache_path] + code <<-EOF + tar -jxvf Python-#{version}.tar.bz2 + (cd Python-#{version} && ./configure #{configure_options}) + (cd Python-#{version} && make && make install) + EOF + environment({ + "LDFLAGS" => "-L#{node['python']['prefix_dir']} -L/usr/lib", + "CPPFLAGS" => "-I#{node['python']['prefix_dir']} -I/usr/lib", + "CXXFLAGS" => "-I#{node['python']['prefix_dir']} -I/usr/lib", + "CFLAGS" => "-I#{node['python']['prefix_dir']} -I/usr/lib" + }) if platform?("ubuntu") && node['platform_version'].to_f >= 12.04 + not_if { ::File.exists?(install_path) } +end diff --git a/chef/cookbooks/python/recipes/virtualenv.rb b/chef/cookbooks/python/recipes/virtualenv.rb new file mode 100644 index 0000000..4c28f80 --- /dev/null +++ b/chef/cookbooks/python/recipes/virtualenv.rb @@ -0,0 +1,25 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Recipe:: virtualenv +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "python::pip" + +python_pip "virtualenv" do + action :install +end diff --git a/chef/cookbooks/python/resources/pip.rb b/chef/cookbooks/python/resources/pip.rb new file mode 100644 index 0000000..cccb224 --- /dev/null +++ b/chef/cookbooks/python/resources/pip.rb @@ -0,0 +1,36 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Resource:: pip +# +# Copyright:: 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :install, :upgrade, :remove, :purge +default_action :install if defined?(default_action) # Chef > 10.8 + +# Default action for Chef <= 10.8 +def initialize(*args) + super + @action = :install +end + +attribute :package_name, :kind_of => String, :name_attribute => true +attribute :version, :default => nil +attribute :timeout, :default => 900 +attribute :virtualenv, :kind_of => String +attribute :user, :regex => Chef::Config[:user_valid_regex] +attribute :group, :regex => Chef::Config[:group_valid_regex] +attribute :options, :kind_of => String, :default => '' diff --git a/chef/cookbooks/python/resources/virtualenv.rb b/chef/cookbooks/python/resources/virtualenv.rb new file mode 100644 index 0000000..e9f7327 --- /dev/null +++ b/chef/cookbooks/python/resources/virtualenv.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore +# Cookbook Name:: python +# Resource:: virtualenv +# +# Copyright:: 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :create, :delete +default_action :create if defined?(default_action) # Chef > 10.8 + +# Default action for Chef <= 10.8 +def initialize(*args) + super + @action = :create +end + +attribute :path, :kind_of => String, :name_attribute => true +attribute :interpreter, :kind_of => String +attribute :owner, :regex => Chef::Config[:user_valid_regex] +attribute :group, :regex => Chef::Config[:group_valid_regex] +attribute :options, :kind_of => String diff --git a/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py b/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py new file mode 100644 index 0000000..325e4f5 --- /dev/null +++ b/chef/cookbooks/python/src/1/calculating_with_dictionaries/example.py @@ -0,0 +1,25 @@ +# example.py +# +# Example of calculating with dictionaries + +prices = { + 'ACME': 45.23, + 'AAPL': 612.78, + 'IBM': 205.55, + 'HPQ': 37.20, + 'FB': 10.75 +} + +# Find min and max price +min_price = min(zip(prices.values(), prices.keys())) +max_price = max(zip(prices.values(), prices.keys())) + +print('min price:', min_price) +print('max price:', max_price) + +print('sorted prices:') +prices_sorted = sorted(zip(prices.values(), prices.keys())) +for price, name in prices_sorted: + print(' ', name, price) + + diff --git a/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py b/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py new file mode 100644 index 0000000..3906136 --- /dev/null +++ b/chef/cookbooks/python/src/1/determine_the_top_n_items_occurring_in_a_list/example.py @@ -0,0 +1,25 @@ +# example.py +# +# Determine the most common words in a list + +words = [ + 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', + 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', + 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', + 'my', 'eyes', "you're", 'under' +] + +from collections import Counter +word_counts = Counter(words) +top_three = word_counts.most_common(3) +print(top_three) +# outputs [('eyes', 8), ('the', 5), ('look', 4)] + +# Example of merging in more words + +morewords = ['why','are','you','not','looking','in','my','eyes'] +word_counts.update(morewords) +print(word_counts.most_common(3)) + + + diff --git a/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py b/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py new file mode 100644 index 0000000..ca962c6 --- /dev/null +++ b/chef/cookbooks/python/src/1/extracting_a_subset_of_a_dictionary/example.py @@ -0,0 +1,23 @@ +# example of extracting a subset from a dictionary +from pprint import pprint + +prices = { + 'ACME': 45.23, + 'AAPL': 612.78, + 'IBM': 205.55, + 'HPQ': 37.20, + 'FB': 10.75 +} + +# Make a dictionary of all prices over 200 +p1 = { key:value for key, value in prices.items() if value > 200 } + +print("All prices over 200") +pprint(p1) + +# Make a dictionary of tech stocks +tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' } +p2 = { key:value for key,value in prices.items() if key in tech_names } + +print("All techs") +pprint(p2) diff --git a/chef/cookbooks/python/src/1/filtering_list_elements/example.py b/chef/cookbooks/python/src/1/filtering_list_elements/example.py new file mode 100644 index 0000000..7cba0b2 --- /dev/null +++ b/chef/cookbooks/python/src/1/filtering_list_elements/example.py @@ -0,0 +1,43 @@ +# Examples of different ways to filter data + +mylist = [1, 4, -5, 10, -7, 2, 3, -1] + +# All positive values +pos = [n for n in mylist if n > 0] +print(pos) + +# All negative values +neg = [n for n in mylist if n < 0] +print(neg) + +# Negative values clipped to 0 +neg_clip = [n if n > 0 else 0 for n in mylist] +print(neg_clip) + +# Positive values clipped to 0 +pos_clip = [n if n < 0 else 0 for n in mylist] +print(pos_clip) + +# Compressing example + +addresses = [ + '5412 N CLARK', + '5148 N CLARK', + '5800 E 58TH', + '2122 N CLARK', + '5645 N RAVENSWOOD', + '1060 W ADDISON', + '4801 N BROADWAY', + '1039 W GRANVILLE', +] + +counts = [ 0, 3, 10, 4, 1, 7, 6, 1] + +from itertools import compress + +more5 = [ n > 5 for n in counts ] +a = list(compress(addresses, more5)) +print(a) + + + diff --git a/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py b/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py new file mode 100644 index 0000000..89a083f --- /dev/null +++ b/chef/cookbooks/python/src/1/finding_out_what_two_dictionaries_have_in_common/example.py @@ -0,0 +1,20 @@ +# example.py +# +# Find out what two dictionaries have in common + +a = { + 'x' : 1, + 'y' : 2, + 'z' : 3 +} + +b = { + 'w' : 10, + 'x' : 11, + 'y' : 2 +} + +print('Common keys:', a.keys() & b.keys()) +print('Keys in a not in b:', a.keys() - b.keys()) +print('(key,value) pairs in common:', a.items() & b.items()) + diff --git a/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py b/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py new file mode 100644 index 0000000..23ab769 --- /dev/null +++ b/chef/cookbooks/python/src/1/finding_the_largest_or_smallest_n_items/example.py @@ -0,0 +1,20 @@ +# example.py +# +# Example of using heapq to find the N smallest or largest items + +import heapq + +portfolio = [ + {'name': 'IBM', 'shares': 100, 'price': 91.1}, + {'name': 'AAPL', 'shares': 50, 'price': 543.22}, + {'name': 'FB', 'shares': 200, 'price': 21.09}, + {'name': 'HPQ', 'shares': 35, 'price': 31.75}, + {'name': 'YHOO', 'shares': 45, 'price': 16.35}, + {'name': 'ACME', 'shares': 75, 'price': 115.65} +] + +cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price']) +expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price']) + +print(cheap) +print(expensive) diff --git a/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py b/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py new file mode 100644 index 0000000..290a186 --- /dev/null +++ b/chef/cookbooks/python/src/1/grouping-records-together-based-on-a-field/grouping.py @@ -0,0 +1,33 @@ +rows = [ + {'address': '5412 N CLARK', 'date': '07/01/2012'}, + {'address': '5148 N CLARK', 'date': '07/04/2012'}, + {'address': '5800 E 58TH', 'date': '07/02/2012'}, + {'address': '2122 N CLARK', 'date': '07/03/2012'}, + {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}, + {'address': '1060 W ADDISON', 'date': '07/02/2012'}, + {'address': '4801 N BROADWAY', 'date': '07/01/2012'}, + {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}, +] + +from itertools import groupby + +rows.sort(key=lambda r: r['date']) +for date, items in groupby(rows, key=lambda r: r['date']): + print(date) + for i in items: + print(' ', i) + +# Example of building a multidict +from collections import defaultdict +rows_by_date = defaultdict(list) +for row in rows: + rows_by_date[row['date']].append(row) + +for r in rows_by_date['07/01/2012']: + print(r) + + + + + + diff --git a/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py b/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py new file mode 100644 index 0000000..7117b69 --- /dev/null +++ b/chef/cookbooks/python/src/1/implementing_a_priority_queue/example.py @@ -0,0 +1,35 @@ +# example.py +# +# Example of a priority queue + +import heapq + +class PriorityQueue: + def __init__(self): + self._queue = [] + self._index = 0 + + def push(self, item, priority): + heapq.heappush(self._queue, (-priority, self._index, item)) + self._index += 1 + + def pop(self): + return heapq.heappop(self._queue)[-1] + +# Example use +class Item: + def __init__(self, name): + self.name = name + def __repr__(self): + return 'Item({!r})'.format(self.name) + +q = PriorityQueue() +q.push(Item('foo'), 1) +q.push(Item('bar'), 5) +q.push(Item('spam'), 4) +q.push(Item('grok'), 1) + +print("Should be bar:", q.pop()) +print("Should be spam:", q.pop()) +print("Should be foo:", q.pop()) +print("Should be grok:", q.pop()) diff --git a/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py b/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py new file mode 100644 index 0000000..f3cc450 --- /dev/null +++ b/chef/cookbooks/python/src/1/keeping_the_last_n_items/example.py @@ -0,0 +1,17 @@ +from collections import deque + +def search(lines, pattern, history=5): + previous_lines = deque(maxlen=history) + for line in lines: + if pattern in line: + yield line, previous_lines + previous_lines.append(line) + +# Example use on a file +if __name__ == '__main__': + with open('somefile.txt') as f: + for line, prevlines in search(f, 'python', 5): + for pline in prevlines: + print(pline, end='') + print(line, end='') + print('-'*20) diff --git a/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt b/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt new file mode 100644 index 0000000..0255124 --- /dev/null +++ b/chef/cookbooks/python/src/1/keeping_the_last_n_items/somefile.txt @@ -0,0 +1,86 @@ +=== Keeping the Last N Items + +==== Problem + +You want to keep a limited history of the last few items seen +during iteration or during some other kind of processing. + +==== Solution + +Keeping a limited history is a perfect use for a `collections.deque`. +For example, the following code performs a simple text match on a +sequence of lines and prints the matching line along with the previous +N lines of context when found: + +[source,python] +---- +from collections import deque + +def search(lines, pattern, history=5): + previous_lines = deque(maxlen=history) + for line in lines: + if pattern in line: + for pline in previous_lines: + print(lline, end='') + print(line, end='') + print() + previous_lines.append(line) + +# Example use on a file +if __name__ == '__main__': + with open('somefile.txt') as f: + search(f, 'python', 5) +---- + +==== Discussion + +Using `deque(maxlen=N)` creates a fixed size queue. When new items +are added and the queue is full, the oldest item is automatically +removed. For example: + +[source,pycon] +---- +>>> q = deque(maxlen=3) +>>> q.append(1) +>>> q.append(2) +>>> q.append(3) +>>> q +deque([1, 2, 3], maxlen=3) +>>> q.append(4) +>>> q +deque([2, 3, 4], maxlen=3) +>>> q.append(5) +>>> q +deque([3, 4, 5], maxlen=3) +---- + +Although you could manually perform such operations on a list (e.g., +appending, deleting, etc.), the queue solution is far more elegant and +runs a lot faster. + +More generally, a `deque` can be used whenever you need a simple queue +structure. If you don't give it a maximum size, you get an unbounded +queue that lets you append and pop items on either end. For example: + +[source,pycon] +---- +>>> q = deque() +>>> q.append(1) +>>> q.append(2) +>>> q.append(3) +>>> q +deque([1, 2, 3]) +>>> q.appendleft(4) +>>> q +deque([4, 1, 2, 3]) +>>> q.pop() +3 +>>> q +deque([4, 1, 2]) +>>> q.popleft() +4 +---- + +Adding or popping items from either end of a queue has O(1) +complexity. This is unlike a list where inserting or removing +items from the front of the list is O(N). diff --git a/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py b/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py new file mode 100644 index 0000000..5709457 --- /dev/null +++ b/chef/cookbooks/python/src/1/mapping_names_to_sequence_elements/example1.py @@ -0,0 +1,22 @@ +# example.py + +from collections import namedtuple + +Stock = namedtuple('Stock', ['name', 'shares', 'price']) + +def compute_cost(records): + total = 0.0 + for rec in records: + s = Stock(*rec) + total += s.shares * s.price + return total + +# Some Data +records = [ + ('GOOG', 100, 490.1), + ('ACME', 100, 123.45), + ('IBM', 50, 91.15) +] + +print(compute_cost(records)) + diff --git a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py new file mode 100644 index 0000000..141123e --- /dev/null +++ b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example.py @@ -0,0 +1,15 @@ +# example.py +# +# Remove duplicate entries from a sequence while keeping order + +def dedupe(items): + seen = set() + for item in items: + if item not in seen: + yield item + seen.add(item) + +if __name__ == '__main__': + a = [1, 5, 2, 1, 9, 1, 5, 10] + print(a) + print(list(dedupe(a))) diff --git a/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py new file mode 100644 index 0000000..87cfa48 --- /dev/null +++ b/chef/cookbooks/python/src/1/removing_duplicates_from_a_sequence_while_maintaining_order/example2.py @@ -0,0 +1,23 @@ +# example2.py +# +# Remove duplicate entries from a sequence while keeping order + +def dedupe(items, key=None): + seen = set() + for item in items: + val = item if key is None else key(item) + if val not in seen: + yield item + seen.add(val) + +if __name__ == '__main__': + a = [ + {'x': 2, 'y': 3}, + {'x': 1, 'y': 4}, + {'x': 2, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 10, 'y': 15} + ] + print(a) + print(list(dedupe(a, key=lambda a: (a['x'],a['y'])))) + diff --git a/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py b/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py new file mode 100644 index 0000000..2a27d49 --- /dev/null +++ b/chef/cookbooks/python/src/1/sort_a_list_of_dictionaries_by_a_common_key/example.py @@ -0,0 +1,27 @@ +# example.py +# +# Sort a list of a dicts on a common key + +rows = [ + {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, + {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, + {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, + {'fname': 'Big', 'lname': 'Jones', 'uid': 1004} +] + +from operator import itemgetter + +rows_by_fname = sorted(rows, key=itemgetter('fname')) +rows_by_uid = sorted(rows, key=itemgetter('uid')) + +from pprint import pprint + +print("Sorted by fname:") +pprint(rows_by_fname) + +print("Sorted by uid:") +pprint(rows_by_uid) + +rows_by_lfname = sorted(rows, key=itemgetter('lname','fname')) +print("Sorted by lname,fname:") +pprint(rows_by_lfname) diff --git a/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py b/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py new file mode 100644 index 0000000..be196b7 --- /dev/null +++ b/chef/cookbooks/python/src/1/sort_objects_without_native_comparison_support/example.py @@ -0,0 +1,14 @@ +from operator import attrgetter + +class User: + def __init__(self, user_id): + self.user_id = user_id + def __repr__(self): + return 'User({})'.format(self.user_id) + +# Example +users = [User(23), User(3), User(99)] +print(users) + +# Sort it by user-id +print(sorted(users, key=attrgetter('user_id'))) diff --git a/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py b/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py new file mode 100644 index 0000000..adc2503 --- /dev/null +++ b/chef/cookbooks/python/src/1/transforming_and_reducing_data_at_the_same_time/example.py @@ -0,0 +1,24 @@ +# example.py +# +# Some examples of using generators in arguments + +import os +files = os.listdir(os.path.expanduser('~')) +if any(name.endswith('.py') for name in files): + print('There be python!') +else: + print('Sorry, no python.') + +# Output a tuple as CSV +s = ('ACME', 50, 123.45) +print(','.join(str(x) for x in s)) + +# Data reduction across fields of a data structure +portfolio = [ + {'name':'GOOG', 'shares': 50}, + {'name':'YHOO', 'shares': 75}, + {'name':'AOL', 'shares': 20}, + {'name':'SCOX', 'shares': 65} +] +min_shares = min(s['shares'] for s in portfolio) +print(min_shares) diff --git a/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py b/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py new file mode 100644 index 0000000..23d8a57 --- /dev/null +++ b/chef/cookbooks/python/src/1/unpack_a_fixed_number_of_elements_from_iterables_of_arbitrary_length/example.py @@ -0,0 +1,21 @@ +# example.py +# +# Unpacking of tagged tuples of varying sizes + +records = [ + ('foo', 1, 2), + ('bar', 'hello'), + ('foo', 3, 4), +] + +def do_foo(x,y): + print('foo', x, y) + +def do_bar(s): + print('bar', s) + +for tag, *args in records: + if tag == 'foo': + do_foo(*args) + elif tag == 'bar': + do_bar(*args) diff --git a/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py b/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py new file mode 100644 index 0000000..6bd1533 --- /dev/null +++ b/chef/cookbooks/python/src/1/working_with_multiple_mappings_as_a_single_mapping/example.py @@ -0,0 +1,51 @@ +# example.py +# +# Example of combining dicts into a chainmap + +a = {'x': 1, 'z': 3 } +b = {'y': 2, 'z': 4 } + +# (a) Simple example of combining +from collections import ChainMap +c = ChainMap(a,b) +print(c['x']) # Outputs 1 (from a) +print(c['y']) # Outputs 2 (from b) +print(c['z']) # Outputs 3 (from a) + +# Output some common values +print('len(c):', len(c)) +print('c.keys():', list(c.keys())) +print('c.values():', list(c.values())) + +# Modify some values +c['z'] = 10 +c['w'] = 40 +del c['x'] +print("a:", a) + + +# Example of stacking mappings (like scopes) +values = ChainMap() +values['x'] = 1 + +# Add a new mapping +values = values.new_child() +values['x'] = 2 + +# Add a new mapping +values = values.new_child() +values['x'] = 3 + +print(values) +print(values['x']) + +# Discard last mapping +values = values.parents +print(values) +print(values['x']) + +# Discard last mapping +values = values.parents +print(values) +print(values['x']) + diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py new file mode 100644 index 0000000..6ab1d9e --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/explicit_load.py @@ -0,0 +1,23 @@ +# Example of explicit module loading using imp library + +import imp +import urllib.request +import sys + +def load_module(url): + u = urllib.request.urlopen(url) + source = u.read().decode('utf-8') + mod = sys.modules.setdefault(url, imp.new_module(url)) + code = compile(source, url, 'exec') + mod.__file__ = url + mod.__package__ = '' + exec(code, mod.__dict__) + return mod + +if __name__ == '__main__': + fib = load_module('http://localhost:15000/fib.py') + print(fib.fib(10)) + spam = load_module('http://localhost:15000/spam.py') + spam.hello('Guido') + print(fib) + print(spam) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py new file mode 100644 index 0000000..13014e7 --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/metaexample.py @@ -0,0 +1,16 @@ +# metaexample.py +# +# Example of using a meta-path importer + +# Enable for debugging +if False: + import logging + logging.basicConfig(level=logging.DEBUG) + +import urlimport +urlimport.install_meta('http://localhost:15000') + +import fib +import spam +import grok.blah +print(grok.blah.__file__) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py new file mode 100644 index 0000000..f4d8af2 --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/pathexample.py @@ -0,0 +1,18 @@ +# Example of path-path import hook + +# Enable for debugging +if False: + import logging + logging.basicConfig(level=logging.DEBUG) + +import urlimport +urlimport.install_path_hook() + +import sys +sys.path.append('http://localhost:15000') + +import fib +import spam +import grok.blah +print(grok.blah.__file__) + diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py new file mode 100644 index 0000000..269c28c --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/fib.py @@ -0,0 +1,7 @@ +print("I'm fib") + +def fib(n): + if n < 2: + return 1 + else: + return fib(n-1) + fib(n-2) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py new file mode 100644 index 0000000..13ad048 --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/__init__.py @@ -0,0 +1 @@ +print("I'm grok.__init__") diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py new file mode 100644 index 0000000..8b3733a --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/grok/blah.py @@ -0,0 +1 @@ +print("I'm grok.blah") diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py new file mode 100644 index 0000000..8d75f63 --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/testcode/spam.py @@ -0,0 +1,4 @@ +print("I'm spam") + +def hello(name): + print('Hello %s' % name) diff --git a/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py new file mode 100644 index 0000000..96b6a57 --- /dev/null +++ b/chef/cookbooks/python/src/10/loading_modules_from_a_remote_machine_using_import_hooks/urlimport.py @@ -0,0 +1,227 @@ +# urlimport.py + +import sys +import importlib.abc +import imp +from urllib.request import urlopen +from urllib.error import HTTPError, URLError +from html.parser import HTMLParser + +# Debugging +import logging +log = logging.getLogger(__name__) + +# Get links from a given URL +def _get_links(url): + class LinkParser(HTMLParser): + def handle_starttag(self, tag, attrs): + if tag == 'a': + attrs = dict(attrs) + links.add(attrs.get('href').rstrip('/')) + + links = set() + try: + log.debug('Getting links from %s' % url) + u = urlopen(url) + parser = LinkParser() + parser.feed(u.read().decode('utf-8')) + except Exception as e: + log.debug('Could not get links. %s', e) + log.debug('links: %r', links) + return links + +class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + self._links = { } + self._loaders = { baseurl : UrlModuleLoader(baseurl) } + + def find_module(self, fullname, path=None): + log.debug('find_module: fullname=%r, path=%r', fullname, path) + if path is None: + baseurl = self._baseurl + else: + if not path[0].startswith(self._baseurl): + return None + baseurl = path[0] + + parts = fullname.split('.') + basename = parts[-1] + log.debug('find_module: baseurl=%r, basename=%r', baseurl, basename) + + # Check link cache + if basename not in self._links: + self._links[baseurl] = _get_links(baseurl) + + # Check if it's a package + if basename in self._links[baseurl]: + log.debug('find_module: trying package %r', fullname) + fullurl = self._baseurl + '/' + basename + # Attempt to load the package (which accesses __init__.py) + loader = UrlPackageLoader(fullurl) + try: + loader.load_module(fullname) + self._links[fullurl] = _get_links(fullurl) + self._loaders[fullurl] = UrlModuleLoader(fullurl) + log.debug('find_module: package %r loaded', fullname) + except ImportError as e: + log.debug('find_module: package failed. %s', e) + loader = None + return loader + + # A normal module + filename = basename + '.py' + if filename in self._links[baseurl]: + log.debug('find_module: module %r found', fullname) + return self._loaders[baseurl] + else: + log.debug('find_module: module %r not found', fullname) + return None + + def invalidate_caches(self): + log.debug('invalidating link cache') + self._links.clear() + +# Module Loader for a URL +class UrlModuleLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self._baseurl = baseurl + self._source_cache = {} + + def module_repr(self, module): + return '' % (module.__name__, module.__file__) + + # Required method + def load_module(self, fullname): + code = self.get_code(fullname) + mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) + mod.__file__ = self.get_filename(fullname) + mod.__loader__ = self + mod.__package__ = fullname.rpartition('.')[0] + exec(code, mod.__dict__) + return mod + + # Optional extensions + def get_code(self, fullname): + src = self.get_source(fullname) + return compile(src, self.get_filename(fullname), 'exec') + + def get_data(self, path): + pass + + def get_filename(self, fullname): + return self._baseurl + '/' + fullname.split('.')[-1] + '.py' + + def get_source(self, fullname): + filename = self.get_filename(fullname) + log.debug('loader: reading %r', filename) + if filename in self._source_cache: + log.debug('loader: cached %r', filename) + return self._source_cache[filename] + try: + u = urlopen(filename) + source = u.read().decode('utf-8') + log.debug('loader: %r loaded', filename) + self._source_cache[filename] = source + return source + except (HTTPError, URLError) as e: + log.debug('loader: %r failed. %s', filename, e) + raise ImportError("Can't load %s" % filename) + + def is_package(self, fullname): + return False + +# Package loader for a URL +class UrlPackageLoader(UrlModuleLoader): + def load_module(self, fullname): + mod = super().load_module(fullname) + mod.__path__ = [ self._baseurl ] + mod.__package__ = fullname + + def get_filename(self, fullname): + return self._baseurl + '/' + '__init__.py' + + def is_package(self, fullname): + return True + +# Utility functions for installing/uninstalling the loader +_installed_meta_cache = { } +def install_meta(address): + if address not in _installed_meta_cache: + finder = UrlMetaFinder(address) + _installed_meta_cache[address] = finder + sys.meta_path.append(finder) + log.debug('%r installed on sys.meta_path', finder) + +def remove_meta(address): + if address in _installed_meta_cache: + finder = _installed_meta_cache.pop(address) + sys.meta_path.remove(finder) + log.debug('%r removed from sys.meta_path', finder) + +# Path finder class for a URL +class UrlPathFinder(importlib.abc.PathEntryFinder): + def __init__(self, baseurl): + self._links = None + self._loader = UrlModuleLoader(baseurl) + self._baseurl = baseurl + + def find_loader(self, fullname): + log.debug('find_loader: %r', fullname) + parts = fullname.split('.') + basename = parts[-1] + # Check link cache + if self._links is None: + self._links = [] # See discussion + self._links = _get_links(self._baseurl) + + # Check if it's a package + if basename in self._links: + log.debug('find_loader: trying package %r', fullname) + fullurl = self._baseurl + '/' + basename + # Attempt to load the package (which accesses __init__.py) + loader = UrlPackageLoader(fullurl) + try: + loader.load_module(fullname) + log.debug('find_loader: package %r loaded', fullname) + except ImportError as e: + log.debug('find_loader: %r is a namespace package', fullname) + loader = None + return (loader, [fullurl]) + + # A normal module + filename = basename + '.py' + if filename in self._links: + log.debug('find_loader: module %r found', fullname) + return (self._loader, []) + else: + log.debug('find_loader: module %r not found', fullname) + return (None, []) + + def invalidate_caches(self): + log.debug('invalidating link cache') + self._links = None + +# Check path to see if it looks like a URL +_url_path_cache = {} +def handle_url(path): + if path.startswith(('http://', 'https://')): + log.debug('Handle path? %s. [Yes]', path) + if path in _url_path_cache: + finder = _url_path_cache[path] + else: + finder = UrlPathFinder(path) + _url_path_cache[path] = finder + return finder + else: + log.debug('Handle path? %s. [No]', path) + +def install_path_hook(): + sys.path_hooks.append(handle_url) + sys.path_importer_cache.clear() + log.debug('Installing handle_url') + +def remove_path_hook(): + sys.path_hooks.remove(handle_url) + sys.path_importer_cache.clear() + log.debug('Removing handle_url') diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py new file mode 100644 index 0000000..deac877 --- /dev/null +++ b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/bar-package/spam/grok.py @@ -0,0 +1 @@ +print('bar-package grok!') diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py new file mode 100644 index 0000000..3a56460 --- /dev/null +++ b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/example.py @@ -0,0 +1,4 @@ +import sys +sys.path.extend(['foo-package', 'bar-package']) +import spam.blah +import spam.grok diff --git a/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py new file mode 100644 index 0000000..fe4d20c --- /dev/null +++ b/chef/cookbooks/python/src/10/making_separate_directories_import_under_a_common_namespace/foo-package/spam/blah.py @@ -0,0 +1 @@ +print('foo-package blah!') diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py new file mode 100644 index 0000000..7ce1992 --- /dev/null +++ b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example1.py @@ -0,0 +1,8 @@ +from postimport import when_imported + +@when_imported('threading') +def warn_threads(mod): + print('Threads? Are you crazy?') + +if __name__ == '__main__': + import threading diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py new file mode 100644 index 0000000..1083531 --- /dev/null +++ b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/example2.py @@ -0,0 +1,22 @@ +from postimport import when_imported +from functools import wraps + + +def logged(func): + @wraps(func) + def wrapper(*args, **kwargs): + print('Calling', func.__name__, args, kwargs) + return func(*args, **kwargs) + return wrapper + +# Example +@when_imported('math') +def add_logging(mod): + mod.cos = logged(mod.cos) + mod.sin = logged(mod.sin) + +if __name__ == '__main__': + import math + print(math.cos(2)) + print(math.sin(2)) + diff --git a/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py new file mode 100644 index 0000000..66b60ad --- /dev/null +++ b/chef/cookbooks/python/src/10/monkeypatching_modules_on_import/postimport.py @@ -0,0 +1,40 @@ +# postimport.py + +import importlib +import sys +from collections import defaultdict + +_post_import_hooks = defaultdict(list) + +class PostImportFinder: + def __init__(self): + self._skip = set() + + def find_module(self, fullname, path=None): + if fullname in self._skip: + return None + self._skip.add(fullname) + return PostImportLoader(self) + +class PostImportLoader: + def __init__(self, finder): + self._finder = finder + + def load_module(self, fullname): + importlib.import_module(fullname) + module = sys.modules[fullname] + for func in _post_import_hooks[fullname]: + func(module) + self._finder._skip.remove(fullname) + return module + +def when_imported(fullname): + def decorate(func): + if fullname in sys.modules: + func(sys.modules[fullname]) + else: + _post_import_hooks[fullname].append(func) + return func + return decorate + +sys.meta_path.insert(0, PostImportFinder()) diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py new file mode 100644 index 0000000..5fe4ee3 --- /dev/null +++ b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/example.py @@ -0,0 +1,6 @@ +import mymodule +a = mymodule.A() +a.spam() + +b = mymodule.B() +b.bar() diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py new file mode 100644 index 0000000..99152c1 --- /dev/null +++ b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/__init__.py @@ -0,0 +1,5 @@ +# __init__.py + +from .a import A +from .b import B + diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py new file mode 100644 index 0000000..f34204f --- /dev/null +++ b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/a.py @@ -0,0 +1,6 @@ +# a.py + +class A: + def spam(self): + print('A.spam') + diff --git a/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py new file mode 100644 index 0000000..28a0dbf --- /dev/null +++ b/chef/cookbooks/python/src/10/splitting_a_module_into_multiple_files/mymodule/b.py @@ -0,0 +1,8 @@ +# b.py + +from .a import A + +class B(A): + def bar(self): + print('B.bar') + diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py new file mode 100644 index 0000000..5c7c8fd --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoclient.py @@ -0,0 +1,25 @@ +# echoclient.py +# +# An example of a client that connects to an SSL server +# and verifies its certificate + +from socket import socket, AF_INET, SOCK_STREAM +import ssl + +s = socket(AF_INET, SOCK_STREAM) + +# Wrap with an SSL layer and require the server to present its certificate +ssl_s = ssl.wrap_socket(s, + cert_reqs=ssl.CERT_REQUIRED, + ca_certs='server_cert.pem', + ) + +ssl_s.connect(('localhost', 20000)) + +# Communicate with the server +ssl_s.send(b'Hello World!') +resp = ssl_s.recv(8192) +print('Got:', resp) + +# Done +ssl_s.close() diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py new file mode 100644 index 0000000..eb77555 --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/echoserv.py @@ -0,0 +1,38 @@ +from socket import socket, AF_INET, SOCK_STREAM +from socket import SOL_SOCKET, SO_REUSEADDR +import ssl + +KEYFILE = 'server_key.pem' # Private key of the server +CERTFILE = 'server_cert.pem' # Server certificate (given to client) + +def echo_client(s): + while True: + data = s.recv(8192) + if data == b'': + break + s.send(data) + s.close() + print('Connection closed') + +def echo_server(address): + s = socket(AF_INET, SOCK_STREAM) + s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) + s.bind(address) + s.listen(1) + + # Wrap with an SSL layer requiring client certs + s_ssl = ssl.wrap_socket(s, + keyfile=KEYFILE, + certfile=CERTFILE, + server_side=True + ) + # Wait for connections + while True: + try: + c,a = s_ssl.accept() + print('Got connection', c, a) + echo_client(c) + except Exception as e: + print('{}: {}'.format(e.__class__.__name__, e)) + +echo_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh new file mode 100644 index 0000000..32d3e9f --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/makecerts.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +openssl req -new -x509 -days 365 -nodes -out server_cert.pem -keyout server_key.pem +openssl req -new -x509 -days 365 -nodes -out client_cert.pem -keyout client_key.pem diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py new file mode 100644 index 0000000..12bc378 --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_client.py @@ -0,0 +1,35 @@ +# ssl_xmlrpc_client.py +# +# An XML-RPC client that verifies the server certificate + +from xmlrpc.client import SafeTransport, ServerProxy +import ssl + +class VerifyCertSafeTransport(SafeTransport): + def __init__(self, cafile, certfile=None, keyfile=None): + super().__init__() + self._ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self._ssl_context.load_verify_locations(cafile) + if certfile: + self._ssl_context.load_cert_chain(certfile, keyfile) + self._ssl_context.verify_mode = ssl.CERT_REQUIRED + + def make_connection(self, host): + s = super().make_connection((host, {'context': self._ssl_context})) + + return s + +# Create the client proxy +s = ServerProxy('https://localhost:15000', + transport=VerifyCertSafeTransport('server_cert.pem', 'client_cert.pem', 'client_key.pem'), +# transport=VerifyCertSafeTransport('server_cert.pem'), + allow_none=True) + +s.set('foo', 'bar') +s.set('spam', [1, 2, 3]) +print(s.keys()) +print(s.get('foo')) +print(s.get('spam')) +s.delete('spam') +print(s.exists('spam')) + diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py new file mode 100644 index 0000000..f3f3b36 --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/ssl_xmlrpc_server.py @@ -0,0 +1,49 @@ +# ssl_xmlrpc_server.py +# +# An example of an SSL-XMLRPC Server. + +import ssl +from xmlrpc.server import SimpleXMLRPCServer +from sslmixin import SSLMixin + +class SSLSimpleXMLRPCServer(SSLMixin, SimpleXMLRPCServer): + pass + +class KeyValueServer: + _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] + def __init__(self, *args, **kwargs): + self._data = {} + self._serv = SSLSimpleXMLRPCServer(*args, allow_none=True, **kwargs) + for name in self._rpc_methods_: + self._serv.register_function(getattr(self, name)) + + def get(self, name): + return self._data[name] + + def set(self, name, value): + self._data[name] = value + + def delete(self, name): + del self._data[name] + + def exists(self, name): + return name in self._data + + def keys(self): + return list(self._data) + + def serve_forever(self): + self._serv.serve_forever() + +if __name__ == '__main__': + KEYFILE='server_key.pem' # Private key of the server + CERTFILE='server_cert.pem' # Server certificate + CA_CERTS='client_cert.pem' # Certificates of accepted clients + + kvserv = KeyValueServer(('', 15000), + keyfile=KEYFILE, + certfile=CERTFILE, + ca_certs=CA_CERTS, + cert_reqs=ssl.CERT_REQUIRED, + ) + kvserv.serve_forever() diff --git a/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py new file mode 100644 index 0000000..da19e8c --- /dev/null +++ b/chef/cookbooks/python/src/11/adding_ssl_to_network_servers/sslmixin.py @@ -0,0 +1,26 @@ +import ssl + +class SSLMixin: + def __init__(self, *args, + keyfile=None, certfile=None, ca_certs=None, cert_reqs=ssl.CERT_NONE, + **kwargs): + self._keyfile = keyfile + self._certfile = certfile + self._ca_certs = ca_certs + self._cert_reqs = cert_reqs + super().__init__(*args, **kwargs) + + def get_request(self): + client, addr = super().get_request() + client_ssl = ssl.wrap_socket(client, + keyfile = self._keyfile, + certfile = self._certfile, + ca_certs = self._ca_certs, + cert_reqs = self._cert_reqs, + server_side = True) + return client_ssl, addr + + + + + diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py new file mode 100644 index 0000000..7b4f9ef --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/client1.py @@ -0,0 +1,7 @@ +from urllib.request import urlopen + +u = urlopen('http://localhost:8080/hello?name=Guido') +print(u.read().decode('utf-8')) + +u = urlopen('http://localhost:8080/localtime') +print(u.read().decode('utf-8')) diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py new file mode 100644 index 0000000..75b24d0 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/example1.py @@ -0,0 +1,47 @@ +import time + +_hello_resp = '''\ + + + Hello {name} + + +

Hello {name}!

+ +''' + +def hello_world(environ, start_response): + start_response('200 OK', [ ('Content-type','text/html')]) + params = environ['params'] + resp = _hello_resp.format(name=params.get('name')) + yield resp.encode('utf-8') + +_localtime_resp = '''\ + +''' + +def localtime(environ, start_response): + start_response('200 OK', [ ('Content-type', 'application/xml') ]) + resp = _localtime_resp.format(t=time.localtime()) + yield resp.encode('utf-8') + +if __name__ == '__main__': + from resty import PathDispatcher + from wsgiref.simple_server import make_server + + # Create the dispatcher and register functions + dispatcher = PathDispatcher() + dispatcher.register('GET', '/hello', hello_world) + dispatcher.register('GET', '/localtime', localtime) + + # Launch a basic server + httpd = make_server('', 8080, dispatcher) + print('Serving on port 8080...') + httpd.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py new file mode 100644 index 0000000..5e979db --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_simple_rest_based_interface/resty.py @@ -0,0 +1,24 @@ +# resty.py + +import cgi + +def notfound_404(environ, start_response): + start_response('404 Not Found', [ ('Content-type', 'text/plain') ]) + return [b'Not Found'] + +class PathDispatcher: + def __init__(self): + self.pathmap = { } + + def __call__(self, environ, start_response): + path = environ['PATH_INFO'] + params = cgi.FieldStorage(environ['wsgi.input'], + environ=environ) + method = environ['REQUEST_METHOD'].lower() + environ['params'] = { key: params.getvalue(key) for key in params } + handler = self.pathmap.get((method,path), notfound_404) + return handler(environ, start_response) + + def register(self, method, path, function): + self.pathmap[method.lower(), path] = function + return function diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py new file mode 100644 index 0000000..a389866 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoclient.py @@ -0,0 +1,9 @@ +from socket import socket, AF_INET, SOCK_STREAM +s = socket(AF_INET, SOCK_STREAM) +s.connect(('localhost', 20000)) + +s.send(b'Hello\n') +resp = s.recv(8192) +print('Response:', resp) +s.close() + diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py new file mode 100644 index 0000000..b2507d4 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv.py @@ -0,0 +1,15 @@ +from socketserver import BaseRequestHandler, TCPServer + +class EchoHandler(BaseRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + while True: + msg = self.request.recv(8192) + if not msg: + break + self.request.send(msg) + +if __name__ == '__main__': + serv = TCPServer(('', 20000), EchoHandler) + print('Echo server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py new file mode 100644 index 0000000..b2507d4 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv1.py @@ -0,0 +1,15 @@ +from socketserver import BaseRequestHandler, TCPServer + +class EchoHandler(BaseRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + while True: + msg = self.request.recv(8192) + if not msg: + break + self.request.send(msg) + +if __name__ == '__main__': + serv = TCPServer(('', 20000), EchoHandler) + print('Echo server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py new file mode 100644 index 0000000..8574dce --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv2.py @@ -0,0 +1,14 @@ +from socketserver import StreamRequestHandler, TCPServer + +class EchoHandler(StreamRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + # self.rfile is a file-like object for reading + for line in self.rfile: + # self.wfile is a file-like object for writing + self.wfile.write(line) + +if __name__ == '__main__': + serv = TCPServer(('', 20000), EchoHandler) + print('Echo server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py new file mode 100644 index 0000000..24ca788 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv3.py @@ -0,0 +1,21 @@ +from socketserver import StreamRequestHandler, TCPServer + +class EchoHandler(StreamRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + # self.rfile is a file-like object for reading + for line in self.rfile: + # self.wfile is a file-like object for writing + self.wfile.write(line) + +if __name__ == '__main__': + import socket + + serv = TCPServer(('', 20000), EchoHandler, bind_and_activate=False) + # Set up various socket options + serv.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + # Bind and activate + serv.server_bind() + serv.server_activate() + print('Echo server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py new file mode 100644 index 0000000..e5a83c2 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv4.py @@ -0,0 +1,22 @@ +from socketserver import StreamRequestHandler, TCPServer +import socket + +class EchoHandler(StreamRequestHandler): + timeout = 5 + rbufsize = -1 + wbufsize = 0 + disable_nagle_algorithm = False + def handle(self): + print('Got connection from', self.client_address) + # self.rfile is a file-like object for reading + try: + for line in self.rfile: + # self.wfile is a file-like object for writing + self.wfile.write(line) + except socket.timeout: + print('Timed out!') + +if __name__ == '__main__': + serv = TCPServer(('', 20000), EchoHandler) + print('Echo server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py new file mode 100644 index 0000000..44cb709 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/echoserv5.py @@ -0,0 +1,23 @@ +# Echo server using sockets directly + +from socket import socket, AF_INET, SOCK_STREAM + +def echo_handler(address, client_sock): + print('Got connection from {}'.format(address)) + while True: + msg = client_sock.recv(8192) + if not msg: + break + client_sock.sendall(msg) + client_sock.close() + +def echo_server(address, backlog=5): + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(address) + sock.listen(backlog) + while True: + client_sock, client_addr = sock.accept() + echo_handler(client_addr, client_sock) + +if __name__ == '__main__': + echo_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py b/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py new file mode 100644 index 0000000..70f69ca --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_tcp_server/threadedserv.py @@ -0,0 +1,20 @@ +from socketserver import StreamRequestHandler, TCPServer + +class EchoHandler(StreamRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + # self.rfile is a file-like object for reading + for line in self.rfile: + # self.wfile is a file-like object for writing + self.wfile.write(line) + +if __name__ == '__main__': + from threading import Thread + NWORKERS = 16 + serv = TCPServer(('', 20000), EchoHandler) + for n in range(NWORKERS): + t = Thread(target=serv.serve_forever) + t.daemon = True + t.start() + print('Multithreaded server running on port 20000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/client.py b/chef/cookbooks/python/src/11/creating_a_udp_server/client.py new file mode 100644 index 0000000..6838d36 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_udp_server/client.py @@ -0,0 +1,5 @@ +from socket import socket, AF_INET, SOCK_DGRAM + +s = socket(AF_INET, SOCK_DGRAM) +s.sendto(b'', ('localhost', 20000)) +print(s.recvfrom(8192)) diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py new file mode 100644 index 0000000..99bebf0 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv1.py @@ -0,0 +1,14 @@ +from socketserver import BaseRequestHandler, UDPServer +import time + +class TimeHandler(BaseRequestHandler): + def handle(self): + print('Got connection from', self.client_address) + # Get message and client socket + msg, sock = self.request + resp = time.ctime() + sock.sendto(resp.encode('ascii'), self.client_address) + +if __name__ == '__main__': + serv = UDPServer(('', 20000), TimeHandler) + serv.serve_forever() diff --git a/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py new file mode 100644 index 0000000..d753494 --- /dev/null +++ b/chef/cookbooks/python/src/11/creating_a_udp_server/timeserv2.py @@ -0,0 +1,14 @@ +from socket import socket, AF_INET, SOCK_DGRAM +import time + +def time_server(address): + sock = socket(AF_INET, SOCK_DGRAM) + sock.bind(address) + while True: + msg, addr = sock.recvfrom(8192) + print('Got message from', addr) + resp = time.ctime() + sock.sendto(resp.encode('ascii'), addr) + +if __name__ == '__main__': + time_server(('', 20000)) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py b/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py new file mode 100644 index 0000000..70e8bd1 --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/eventhandler.py @@ -0,0 +1,32 @@ +class EventHandler: + def fileno(self): + 'Return the associated file descriptor' + raise NotImplemented('must implement') + + def wants_to_receive(self): + 'Return True if receiving is allowed' + return False + + def handle_receive(self): + 'Perform the receive operation' + pass + + def wants_to_send(self): + 'Return True if sending is requested' + return False + + def handle_send(self): + 'Send outgoing data' + pass + +import select + +def event_loop(handlers): + while True: + wants_recv = [h for h in handlers if h.wants_to_receive()] + wants_send = [h for h in handlers if h.wants_to_send()] + can_recv, can_send, _ = select.select(wants_recv, wants_send, []) + for h in can_recv: + h.handle_receive() + for h in can_send: + h.handle_send() diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py new file mode 100644 index 0000000..df65e8a --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpclient.py @@ -0,0 +1,7 @@ +from socket import socket, AF_INET, SOCK_STREAM + +s = socket(AF_INET, SOCK_STREAM) +s.connect(('localhost', 16000)) +s.send(b'Hello\n') +print('Got:', s.recv(8192)) +s.close() diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py new file mode 100644 index 0000000..61d9e26 --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/tcpserver.py @@ -0,0 +1,61 @@ +# TCP Example + +import socket +from eventhandler import EventHandler, event_loop + +class TCPServer(EventHandler): + def __init__(self, address, client_handler, handler_list): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + self.sock.bind(address) + self.sock.listen(1) + self.client_handler = client_handler + self.handler_list = handler_list + + def fileno(self): + return self.sock.fileno() + + def wants_to_receive(self): + return True + + def handle_receive(self): + client, addr = self.sock.accept() + # Add the client to the event loop's handler list + self.handler_list.append(self.client_handler(client, self.handler_list)) + +class TCPClient(EventHandler): + def __init__(self, sock, handler_list): + self.sock = sock + self.handler_list = handler_list + self.outgoing = bytearray() + + def fileno(self): + return self.sock.fileno() + + def close(self): + self.sock.close() + # Remove myself from the event loop's handler list + self.handler_list.remove(self) + + def wants_to_send(self): + return True if self.outgoing else False + + def handle_send(self): + nsent = self.sock.send(self.outgoing) + self.outgoing = self.outgoing[nsent:] + +class TCPEchoClient(TCPClient): + def wants_to_receive(self): + return True + + def handle_receive(self): + data = self.sock.recv(8192) + if not data: + self.close() + else: + self.outgoing.extend(data) + +if __name__ == '__main__': + handlers = [] + handlers.append(TCPServer(('',16000), TCPEchoClient, handlers)) + event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py b/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py new file mode 100644 index 0000000..54b647a --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/threadpool.py @@ -0,0 +1,65 @@ +import socket +from concurrent.futures import ThreadPoolExecutor +from eventhandler import EventHandler, event_loop + +class ThreadPoolHandler(EventHandler): + def __init__(self, nworkers): + self.signal_done_sock, self.done_sock = socket.socketpair() + self.pending = [] + self.pool = ThreadPoolExecutor(nworkers) + + def fileno(self): + return self.done_sock.fileno() + + # Callback that executes when the thread is done + def _complete(self, callback, r): + self.pending.append((callback, r.result())) + self.signal_done_sock.send(b'x') + + # Run a function in a thread pool + def run(self, func, args=(), kwargs={},*,callback): + r = self.pool.submit(func, *args, **kwargs) + r.add_done_callback(lambda r: self._complete(callback, r)) + + def wants_to_receive(self): + return True + + # Run callback functions of completed work + def handle_receive(self): + # Invoke all pending callback functions + for callback, result in self.pending: + callback(result) + self.done_sock.recv(1) + self.pending = [] + +# A really bad fibonacci implementation +def fib(n): + if n < 2: + return 1 + else: + return fib(n - 1) + fib(n - 2) + +class UDPServer(EventHandler): + def __init__(self, address): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.bind(address) + + def fileno(self): + return self.sock.fileno() + + def wants_to_receive(self): + return True + +class UDPFibServer(UDPServer): + def handle_receive(self): + msg, addr = self.sock.recvfrom(128) + n = int(msg) + pool.run(fib, (n,), callback=lambda r: self.respond(r, addr)) + + def respond(self, result, addr): + self.sock.sendto(str(result).encode('ascii'), addr) + +if __name__ == '__main__': + pool = ThreadPoolHandler(16) + handlers = [ pool, UDPFibServer(('',16000))] + event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py new file mode 100644 index 0000000..4c0b681 --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/thrpoolclient.py @@ -0,0 +1,6 @@ +from socket import * +sock = socket(AF_INET, SOCK_DGRAM) +for x in range(40): + sock.sendto(str(x).encode('ascii'), ('localhost', 16000)) + resp = sock.recvfrom(8192) + print(resp[0]) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py b/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py new file mode 100644 index 0000000..47f68ca --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/udpclient.py @@ -0,0 +1,7 @@ +from socket import * +s = socket(AF_INET, SOCK_DGRAM) +s.sendto(b'', ('localhost', 14000)) +print(s.recvfrom(128)) + +s.sendto(b'Hello', ('localhost', 15000)) +print(s.recvfrom(128)) diff --git a/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py b/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py new file mode 100644 index 0000000..c476fd1 --- /dev/null +++ b/chef/cookbooks/python/src/11/event_driven_io_explained/udpserver.py @@ -0,0 +1,29 @@ +import socket +import time + +from eventhandler import EventHandler, event_loop + +class UDPServer(EventHandler): + def __init__(self, address): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.bind(address) + + def fileno(self): + return self.sock.fileno() + + def wants_to_receive(self): + return True + +class UDPTimeServer(UDPServer): + def handle_receive(self): + msg, addr = self.sock.recvfrom(1) + self.sock.sendto(time.ctime().encode('ascii'), addr) + +class UDPEchoServer(UDPServer): + def handle_receive(self): + msg, addr = self.sock.recvfrom(8192) + self.sock.sendto(msg, addr) + +if __name__ == '__main__': + handlers = [ UDPTimeServer(('',14000)), UDPEchoServer(('',15000)) ] + event_loop(handlers) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py new file mode 100644 index 0000000..9a6ecbd --- /dev/null +++ b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpclient.py @@ -0,0 +1,22 @@ +import json + +class RPCProxy: + def __init__(self, connection): + self._connection = connection + def __getattr__(self, name): + def do_rpc(*args, **kwargs): + self._connection.send(json.dumps((name, args, kwargs))) + result = json.loads(self._connection.recv()) + return result + return do_rpc + +# Example use +from multiprocessing.connection import Client +c = Client(('localhost', 17000), authkey=b'peekaboo') +proxy = RPCProxy(c) +print(proxy.add(2, 3)) +print(proxy.sub(2, 3)) +try: + print(proxy.sub([1, 2], 4)) +except Exception as e: + print(e) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py new file mode 100644 index 0000000..d87a806 --- /dev/null +++ b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/jsonrpcserver.py @@ -0,0 +1,50 @@ +# rpcserver.py +import json + +class RPCHandler: + def __init__(self): + self._functions = { } + + def register_function(self, func): + self._functions[func.__name__] = func + + def handle_connection(self, connection): + try: + while True: + # Receive a message + func_name, args, kwargs = json.loads(connection.recv()) + # Run the RPC and send a response + try: + r = self._functions[func_name](*args,**kwargs) + connection.send(json.dumps(r)) + except Exception as e: + connection.send(json.dumps(str(e))) + except EOFError: + pass + +# Example use +from multiprocessing.connection import Listener +from threading import Thread + +def rpc_server(handler, address, authkey): + sock = Listener(address, authkey=authkey) + while True: + client = sock.accept() + t = Thread(target=handler.handle_connection, args=(client,)) + t.daemon = True + t.start() + +# Some remote functions +def add(x, y): + return x + y + +def sub(x, y): + return x - y + +# Register with a handler +handler = RPCHandler() +handler.register_function(add) +handler.register_function(sub) + +# Run the server +rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py new file mode 100644 index 0000000..98c2e34 --- /dev/null +++ b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcclient.py @@ -0,0 +1,24 @@ +import pickle + +class RPCProxy: + def __init__(self, connection): + self._connection = connection + def __getattr__(self, name): + def do_rpc(*args, **kwargs): + self._connection.send(pickle.dumps((name, args, kwargs))) + result = pickle.loads(self._connection.recv()) + if isinstance(result, Exception): + raise result + return result + return do_rpc + +# Example use +from multiprocessing.connection import Client +c = Client(('localhost', 17000), authkey=b'peekaboo') +proxy = RPCProxy(c) +print(proxy.add(2, 3)) +print(proxy.sub(2, 3)) +try: + proxy.sub([1, 2], 4) +except Exception as e: + print(e) diff --git a/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py new file mode 100644 index 0000000..2eb2e18 --- /dev/null +++ b/chef/cookbooks/python/src/11/implementing_remote_procedure_call/rpcserver.py @@ -0,0 +1,50 @@ +# rpcserver.py + +import pickle +class RPCHandler: + def __init__(self): + self._functions = { } + + def register_function(self, func): + self._functions[func.__name__] = func + + def handle_connection(self, connection): + try: + while True: + # Receive a message + func_name, args, kwargs = pickle.loads(connection.recv()) + # Run the RPC and send a response + try: + r = self._functions[func_name](*args,**kwargs) + connection.send(pickle.dumps(r)) + except Exception as e: + connection.send(pickle.dumps(e)) + except EOFError: + pass + +# Example use +from multiprocessing.connection import Listener +from threading import Thread + +def rpc_server(handler, address, authkey): + sock = Listener(address, authkey=authkey) + while True: + client = sock.accept() + t = Thread(target=handler.handle_connection, args=(client,)) + t.daemon = True + t.start() + +# Some remote functions +def add(x, y): + return x + y + +def sub(x, y): + return x - y + +# Register with a handler +handler = RPCHandler() +handler.register_function(add) +handler.register_function(sub) + +# Run the server +rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py new file mode 100644 index 0000000..e550bc5 --- /dev/null +++ b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example1.py @@ -0,0 +1,26 @@ +# A basic GET request + +from urllib import request, parse + +# Base URL being accessed +url = 'http://httpbin.org/get' + +# Dictionary of query parameters (if any) +parms = { + 'name1' : 'value1', + 'name2' : 'value2' +} + +# Encode the query string +querystring = parse.urlencode(parms) + +# Make a GET request and read the response +u = request.urlopen(url+'?' + querystring) +resp = u.read() + +import json +from pprint import pprint + +json_resp = json.loads(resp.decode('utf-8')) +pprint(json_resp) + diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py new file mode 100644 index 0000000..ca9a42b --- /dev/null +++ b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example2.py @@ -0,0 +1,26 @@ +# A basic POST request + +from urllib import request, parse + +# Base URL being accessed +url = 'http://httpbin.org/post' + +# Dictionary of query parameters (if any) +parms = { + 'name1' : 'value1', + 'name2' : 'value2' +} + +# Encode the query string +querystring = parse.urlencode(parms) + +# Make a POST request and read the response +u = request.urlopen(url, querystring.encode('ascii')) +resp = u.read() + +import json +from pprint import pprint + +json_resp = json.loads(resp.decode('utf-8')) +pprint(json_resp) + diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py new file mode 100644 index 0000000..02da1ab --- /dev/null +++ b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example3.py @@ -0,0 +1,25 @@ +# A POST request using requests library +import requests + +# Base URL being accessed +url = 'http://httpbin.org/post' + +# Dictionary of query parameters (if any) +parms = { + 'name1' : 'value1', + 'name2' : 'value2' +} + +# Extra headers +headers = { + 'User-agent' : 'none/ofyourbusiness', + 'Spam' : 'Eggs' +} + +resp = requests.post(url, data=parms, headers=headers) + +# Decoded text returned by the request +text = resp.text + +from pprint import pprint +pprint(resp.json) diff --git a/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py new file mode 100644 index 0000000..e8f51fc --- /dev/null +++ b/chef/cookbooks/python/src/11/interacting_with_http_services_as_a_client/example4.py @@ -0,0 +1,15 @@ +# Example of a HEAD request + +import requests + +resp = requests.head('http://www.python.org/index.html') + +status = resp.status_code +last_modified = resp.headers['last-modified'] +content_type = resp.headers['content-type'] +content_length = resp.headers['content-length'] + +print(status) +print(last_modified) +print(content_type) +print(content_length) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py new file mode 100644 index 0000000..004f9b9 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/client1.py @@ -0,0 +1,9 @@ +from socket import socket, AF_INET, SOCK_STREAM + +s = socket(AF_INET, SOCK_STREAM) +s.connect(('localhost', 15000)) +s.send(b'Hello\n') +print('Got:', s.recv(8192)) +s.send(b'World\n') +print('Got:', s.recv(8192)) +s.close() diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py new file mode 100644 index 0000000..2dd6790 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server.py @@ -0,0 +1,38 @@ +# server.py +import socket +import struct + +def send_fd(sock, fd): + ''' + Send a single file descriptor. + ''' + sock.sendmsg([b'x'], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('i', fd))]) + ack = sock.recv(2) + assert ack == b'OK' + +def server(work_address, port): + # Wait for the worker to connect + work_serv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + work_serv.bind(work_address) + work_serv.listen(1) + worker, addr = work_serv.accept() + + # Now run a TCP/IP server and send clients to worker + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + s.bind(('',port)) + s.listen(1) + while True: + client, addr = s.accept() + print('SERVER: Got connection from', addr) + send_fd(worker, client.fileno()) + client.close() + +if __name__ == '__main__': + import sys + if len(sys.argv) != 3: + print('Usage: server.py server_address port', file=sys.stderr) + raise SystemExit(1) + + server(sys.argv[1], int(sys.argv[2])) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py new file mode 100644 index 0000000..3df1d18 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/server1.py @@ -0,0 +1,42 @@ +# Example of file descriptor passing using multiprocessing + +import multiprocessing +from multiprocessing.reduction import recv_handle, send_handle +import socket + +def worker(in_p, out_p): + out_p.close() + while True: + fd = recv_handle(in_p) + print('CHILD: GOT FD', fd) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as s: + while True: + msg = s.recv(1024) + if not msg: + break + print('CHILD: RECV {!r}'.format(msg)) + s.send(msg) + +def server(address, in_p, out_p, worker_pid): + in_p.close() + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + s.bind(address) + s.listen(1) + while True: + client, addr = s.accept() + print('SERVER: Got connection from', addr) + send_handle(out_p, client.fileno(), worker_pid) + client.close() + +if __name__ == '__main__': + c1, c2 = multiprocessing.Pipe() + worker_p = multiprocessing.Process(target=worker, args=(c1,c2)) + worker_p.start() + + server_p = multiprocessing.Process(target=server, + args=(('', 15000), c1, c2, worker_p.pid)) + server_p.start() + + c1.close() + c2.close() diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py new file mode 100644 index 0000000..d7bab24 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/servermp.py @@ -0,0 +1,29 @@ +# servermp.py +from multiprocessing.connection import Listener +from multiprocessing.reduction import send_handle +import socket + +def server(work_address, port): + # Wait for the worker to connect + work_serv = Listener(work_address, authkey=b'peekaboo') + worker = work_serv.accept() + worker_pid = worker.recv() + + # Now run a TCP/IP server and send clients to worker + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + s.bind(('', port)) + s.listen(1) + while True: + client, addr = s.accept() + print('SERVER: Got connection from', addr) + send_handle(worker, client.fileno(), worker_pid) + client.close() + +if __name__ == '__main__': + import sys + if len(sys.argv) != 3: + print('Usage: server.py server_address port', file=sys.stderr) + raise SystemExit(1) + + server(sys.argv[1], int(sys.argv[2])) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py new file mode 100644 index 0000000..2ee5933 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/worker.py @@ -0,0 +1,37 @@ +# worker.py +import socket +import struct + +def recv_fd(sock): + ''' + Receive a single file descriptor + ''' + msg, ancdata, flags, addr = sock.recvmsg(1, + socket.CMSG_LEN(struct.calcsize('i'))) + + cmsg_level, cmsg_type, cmsg_data = ancdata[0] + assert cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS + sock.sendall(b'OK') + return struct.unpack('i', cmsg_data)[0] + +def worker(server_address): + serv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + serv.connect(server_address) + while True: + fd = recv_fd(serv) + print('WORKER: GOT FD', fd) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as client: + while True: + msg = client.recv(1024) + if not msg: + break + print('WORKER: RECV {!r}'.format(msg)) + client.send(msg) + +if __name__ == '__main__': + import sys + if len(sys.argv) != 2: + print('Usage: worker.py server_address', file=sys.stderr) + raise SystemExit(1) + + worker(sys.argv[1]) diff --git a/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py new file mode 100644 index 0000000..4717897 --- /dev/null +++ b/chef/cookbooks/python/src/11/passing_a_socket_file_descriptor_between_processes/workermp.py @@ -0,0 +1,28 @@ +# workermp.py + +from multiprocessing.connection import Client +from multiprocessing.reduction import recv_handle +import os +import socket + +def worker(server_address): + serv = Client(server_address, authkey=b'peekaboo') + serv.send(os.getpid()) + while True: + fd = recv_handle(serv) + print('WORKER: GOT FD', fd) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=fd) as client: + while True: + msg = client.recv(1024) + if not msg: + break + print('WORKER: RECV {!r}'.format(msg)) + client.send(msg) + +if __name__ == '__main__': + import sys + if len(sys.argv) != 2: + print('Usage: worker.py server_address', file=sys.stderr) + raise SystemExit(1) + + worker(sys.argv[1]) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py new file mode 100644 index 0000000..2cb9f89 --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_authentication_of_clients/auth.py @@ -0,0 +1,26 @@ +# auth.py + +import hmac +import os + +def client_authenticate(connection, secret_key): + ''' + Authenticate client to a remote service. + connection represents a network connection. + secret_key is a key known only to both client/server. + ''' + message = connection.recv(32) + hash = hmac.new(secret_key, message) + digest = hash.digest() + connection.send(digest) + +def server_authenticate(connection, secret_key): + ''' + Request client authentication. + ''' + message = os.urandom(32) + connection.send(message) + hash = hmac.new(secret_key, message) + digest = hash.digest() + response = connection.recv(len(digest)) + return hmac.compare_digest(digest,response) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py new file mode 100644 index 0000000..2c82daa --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_authentication_of_clients/client.py @@ -0,0 +1,11 @@ +from socket import socket, AF_INET, SOCK_STREAM +from auth import client_authenticate + +secret_key = b'peekaboo' + +s = socket(AF_INET, SOCK_STREAM) +s.connect(('localhost', 18000)) +client_authenticate(s, secret_key) +s.send(b'Hello World') +resp = s.recv(1024) +print('Got:', resp) diff --git a/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py b/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py new file mode 100644 index 0000000..09f575e --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_authentication_of_clients/server.py @@ -0,0 +1,26 @@ +from socket import socket, AF_INET, SOCK_STREAM +from auth import server_authenticate + +secret_key = b'peekaboo' + +def echo_handler(client_sock): + if not server_authenticate(client_sock, secret_key): + client_sock.close() + return + while True: + msg = client_sock.recv(8192) + if not msg: + break + client_sock.sendall(msg) + +def echo_server(address): + s = socket(AF_INET, SOCK_STREAM) + s.bind(address) + s.listen(5) + while True: + c,a = s.accept() + echo_handler(c) + +print('Echo server running on port 18000') + +echo_server(('', 18000)) diff --git a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py new file mode 100644 index 0000000..79551d1 --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoclient.py @@ -0,0 +1,9 @@ +from multiprocessing.connection import Client +c = Client(('localhost', 25000), authkey=b'peekaboo') +c.send('hello') +print('Got:', c.recv()) +c.send(42) +print('Got:', c.recv()) +c.send([1, 2, 3, 4, 5]) +print('Got:', c.recv()) + diff --git a/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py new file mode 100644 index 0000000..f9659b4 --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_communication_between_interpreters/echoserv.py @@ -0,0 +1,21 @@ +from multiprocessing.connection import Listener +import traceback + +def echo_client(conn): + try: + while True: + msg = conn.recv() + conn.send(msg) + except EOFError: + print('Connection closed') + +def echo_server(address, authkey): + serv = Listener(address, authkey=authkey) + while True: + try: + client = serv.accept() + echo_client(client) + except Exception: + traceback.print_exc() + +echo_server(('', 25000), authkey=b'peekaboo') diff --git a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py new file mode 100644 index 0000000..48685cd --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/client.py @@ -0,0 +1,9 @@ +from xmlrpc.client import ServerProxy +s = ServerProxy('http://localhost:15000', allow_none=True) +s.set('foo', 'bar') +s.set('spam', [1, 2, 3]) +print(s.keys()) +print(s.get('foo')) +print(s.get('spam')) +s.delete('spam') +print(s.exists('spam')) diff --git a/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py new file mode 100644 index 0000000..9d6410c --- /dev/null +++ b/chef/cookbooks/python/src/11/simple_remote_procedure_call_with_xmlrpc/keyserv.py @@ -0,0 +1,32 @@ +from xmlrpc.server import SimpleXMLRPCServer + +class KeyValueServer: + _rpc_methods_ = ['get', 'set', 'delete', 'exists', 'keys'] + def __init__(self, address): + self._data = {} + self._serv = SimpleXMLRPCServer(address, allow_none=True) + for name in self._rpc_methods_: + self._serv.register_function(getattr(self, name)) + + def get(self, name): + return self._data[name] + + def set(self, name, value): + self._data[name] = value + + def delete(self, name): + del self._data[name] + + def exists(self, name): + return name in self._data + + def keys(self): + return list(self._data) + + def serve_forever(self): + self._serv.serve_forever() + +# Example +if __name__ == '__main__': + kvserv = KeyValueServer(('', 15000)) + kvserv.serve_forever() diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py new file mode 100644 index 0000000..48b3e50 --- /dev/null +++ b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/client.py @@ -0,0 +1,12 @@ +from zerocopy import recv_into +from socket import * + +c = socket(AF_INET, SOCK_STREAM) +c.connect(('localhost', 25000)) + +import numpy +a = numpy.zeros(shape=50000000, dtype=float) +print(a[0:10]) +recv_into(a, c) +print(a[0:10]) +print(a[-10:]) diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py new file mode 100644 index 0000000..5e16653 --- /dev/null +++ b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/server.py @@ -0,0 +1,13 @@ +from zerocopy import send_from +from socket import * + +s = socket(AF_INET, SOCK_STREAM) +s.bind(('', 25000)) +s.listen(1) +c,a = s.accept() + +import numpy +a = numpy.arange(0.0, 50000000.0) +send_from(a, c) +c.close() + diff --git a/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py new file mode 100644 index 0000000..42253ec --- /dev/null +++ b/chef/cookbooks/python/src/11/zero_copy_sending_and_receiving_of_large_arrays/zerocopy.py @@ -0,0 +1,13 @@ +# zerocopy.py + +def send_from(arr, dest): + view = memoryview(arr).cast('B') + while len(view): + nsent = dest.send(view) + view = view[nsent:] + +def recv_into(arr, source): + view = memoryview(arr).cast('B') + while len(view): + nrecv = source.recv_into(view) + view = view[nrecv:] diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py b/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py new file mode 100644 index 0000000..735a4f3 --- /dev/null +++ b/chef/cookbooks/python/src/12/defining_an_actor_task/actor.py @@ -0,0 +1,75 @@ +from queue import Queue +from threading import Thread, Event + +# Sentinel used for shutdown +class ActorExit(Exception): + pass + +class Actor: + def __init__(self): + self._mailbox = Queue() + + def send(self, msg): + ''' + Send a message to the actor + ''' + self._mailbox.put(msg) + + def recv(self): + ''' + Receive an incoming message + ''' + msg = self._mailbox.get() + if msg is ActorExit: + raise ActorExit() + return msg + + def close(self): + ''' + Close the actor, thus shutting it down + ''' + self.send(ActorExit) + + def start(self): + ''' + Start concurrent execution + ''' + self._terminated = Event() + t = Thread(target=self._bootstrap) + t.daemon = True + t.start() + + def _bootstrap(self): + try: + self.run() + except ActorExit: + pass + finally: + self._terminated.set() + + def join(self): + self._terminated.wait() + + def run(self): + ''' + Run method to be implemented by the user + ''' + while True: + msg = self.recv() + +# Sample ActorTask +class PrintActor(Actor): + def run(self): + while True: + msg = self.recv() + print("Got:", msg) + +if __name__ == '__main__': + # Sample use + p = PrintActor() + p.start() + p.send("Hello") + p.send("World") + p.close() + p.join() + diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py b/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py new file mode 100644 index 0000000..85ca6bb --- /dev/null +++ b/chef/cookbooks/python/src/12/defining_an_actor_task/tagged.py @@ -0,0 +1,24 @@ +from actor import Actor + +class TaggedActor(Actor): + def run(self): + while True: + tag, *payload = self.recv() + getattr(self,"do_"+tag)(*payload) + + # Methods correponding to different message tags + def do_A(self, x): + print("Running A", x) + + def do_B(self, x, y): + print("Running B", x, y) + +# Example +if __name__ == '__main__': + a = TaggedActor() + a.start() + a.send(('A', 1)) # Invokes do_A(1) + a.send(('B', 2, 3)) # Invokes do_B(2,3) + a.close() + a.join() + diff --git a/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py b/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py new file mode 100644 index 0000000..22981de --- /dev/null +++ b/chef/cookbooks/python/src/12/defining_an_actor_task/worker.py @@ -0,0 +1,36 @@ +from actor import Actor +from threading import Event + +class Result: + def __init__(self): + self._evt = Event() + self._result = None + + def set_result(self, value): + self._result = value + self._evt.set() + + def result(self): + self._evt.wait() + return self._result + +class Worker(Actor): + def submit(self, func, *args, **kwargs): + r = Result() + self.send((func, args, kwargs, r)) + return r + + def run(self): + while True: + func, args, kwargs, r = self.recv() + r.set_result(func(*args, **kwargs)) + +# Example use +if __name__ == '__main__': + worker = Worker() + worker.start() + r = worker.submit(pow, 2, 3) + print(r.result()) + worker.close() + worker.join() + diff --git a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py new file mode 100644 index 0000000..d96cf30 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example1.py @@ -0,0 +1,43 @@ +from queue import Queue +from threading import Thread +import time + +_sentinel = object() + +# A thread that produces data +def producer(out_q): + n = 10 + while n > 0: + # Produce some data + out_q.put(n) + time.sleep(2) + n -= 1 + + + # Put the sentinel on the queue to indicate completion + out_q.put(_sentinel) + +# A thread that consumes data +def consumer(in_q): + while True: + # Get some data + data = in_q.get() + + # Check for termination + if data is _sentinel: + in_q.put(_sentinel) + break + + # Process the data + print('Got:', data) + print('Consumer shutting down') + +if __name__ == '__main__': + q = Queue() + t1 = Thread(target=consumer, args=(q,)) + t2 = Thread(target=producer, args=(q,)) + t1.start() + t2.start() + t1.join() + t2.join() + diff --git a/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py new file mode 100644 index 0000000..80fa230 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_communicate_between_threads/example2.py @@ -0,0 +1,49 @@ +import heapq +import threading +import time + +class PriorityQueue: + def __init__(self): + self._queue = [] + self._count = 0 + self._cv = threading.Condition() + def put(self, item, priority): + with self._cv: + heapq.heappush(self._queue, (-priority, self._count, item)) + self._count += 1 + self._cv.notify() + + def get(self): + with self._cv: + while len(self._queue) == 0: + self._cv.wait() + return heapq.heappop(self._queue)[-1] + +def producer(q): + print('Producing items') + q.put('C', 5) + q.put('A', 15) + q.put('B', 10) + q.put('D', 0) + q.put(None, -100) + +def consumer(q): + time.sleep(5) + print('Getting items') + while True: + item = q.get() + if item is None: + break + print('Got:', item) + print('Consumer done') + +if __name__ == '__main__': + q = PriorityQueue() + t1 = threading.Thread(target=producer, args=(q,)) + t2 = threading.Thread(target=consumer, args=(q,)) + t1.start() + t2.start() + t1.join() + t2.join() + + diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py new file mode 100644 index 0000000..1b13c46 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example1.py @@ -0,0 +1,27 @@ +from socket import AF_INET, SOCK_STREAM, socket +from concurrent.futures import ThreadPoolExecutor + +def echo_client(sock, client_addr): + ''' + Handle a client connection + ''' + print('Got connection from', client_addr) + while True: + msg = sock.recv(65536) + if not msg: + break + sock.sendall(msg) + print('Client closed connection') + sock.close() + +def echo_server(addr): + print('Echo server running at', addr) + pool = ThreadPoolExecutor(128) + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(addr) + sock.listen(5) + while True: + client_sock, client_addr = sock.accept() + pool.submit(echo_client, client_sock, client_addr) + +echo_server(('',15000)) diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py new file mode 100644 index 0000000..6ae0c94 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example2.py @@ -0,0 +1,36 @@ +from socket import socket, AF_INET, SOCK_STREAM +from threading import Thread +from queue import Queue + +def echo_client(q): + ''' + Handle a client connection + ''' + sock, client_addr = q.get() + print('Got connection from', client_addr) + while True: + msg = sock.recv(65536) + if not msg: + break + sock.sendall(msg) + print('Client closed connection') + sock.close() + +def echo_server(addr, nworkers): + print('Echo server running at', addr) + # Launch the client workers + q = Queue() + for n in range(nworkers): + t = Thread(target=echo_client, args=(q,)) + t.daemon = True + t.start() + + # Run the server + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(addr) + sock.listen(5) + while True: + client_sock, client_addr = sock.accept() + q.put((client_sock, client_addr)) + +echo_server(('',15000), 128) diff --git a/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py new file mode 100644 index 0000000..c9f76e8 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_create_a_thread_pool/example3.py @@ -0,0 +1,16 @@ +from concurrent.futures import ThreadPoolExecutor +import urllib.request + +def fetch_url(url): + u = urllib.request.urlopen(url) + data = u.read() + return data + +pool = ThreadPoolExecutor(10) +# Submit work to the pool +a = pool.submit(fetch_url, 'http://www.python.org') +b = pool.submit(fetch_url, 'http://www.pypy.org') + +# Get the results back +x = a.result() +y = b.result() diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py new file mode 100644 index 0000000..bb73197 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example1.py @@ -0,0 +1,23 @@ +from threading import Thread, Event +import time + +# Code to execute in an independent thread +def countdown(n, started_evt): + print("countdown starting") + started_evt.set() + while n > 0: + print("T-minus", n) + n -= 1 + time.sleep(5) + +# Create the event object that will be used to signal startup +started_evt = Event() + +# Launch the thread and pass the startup event +print("Launching countdown") +t = Thread(target=countdown, args=(10,started_evt)) +t.start() + +# Wait for the thread to start +started_evt.wait() +print("countdown is running") diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py new file mode 100644 index 0000000..3802f34 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example2.py @@ -0,0 +1,53 @@ +import threading +import time + +class PeriodicTimer: + def __init__(self, interval): + self._interval = interval + self._flag = 0 + self._cv = threading.Condition() + + def start(self): + t = threading.Thread(target=self.run) + t.daemon = True + t.start() + + def run(self): + ''' + Run the timer and notify waiting threads after each interval + ''' + while True: + time.sleep(self._interval) + with self._cv: + self._flag ^= 1 + self._cv.notify_all() + + def wait_for_tick(self): + ''' + Wait for the next tick of the timer + ''' + with self._cv: + last_flag = self._flag + while last_flag == self._flag: + self._cv.wait() + +# Example use of the timer +ptimer = PeriodicTimer(5) +ptimer.start() + +# Two threads that synchronize on the timer +def countdown(nticks): + while nticks > 0: + ptimer.wait_for_tick() + print("T-minus", nticks) + nticks -= 1 + +def countup(last): + n = 0 + while n < last: + ptimer.wait_for_tick() + print("Counting", n) + n += 1 + +threading.Thread(target=countdown, args=(10,)).start() +threading.Thread(target=countup, args=(5,)).start() diff --git a/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py new file mode 100644 index 0000000..de742ed --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_determine_if_a_thread_has_started/example3.py @@ -0,0 +1,27 @@ +import threading +import time + +# Worker thread +def worker(n, sema): + # Wait to be signalled + sema.acquire() + # Do some work + print("Working", n) + +# Create some threads +sema = threading.Semaphore(0) +nworkers = 10 +for n in range(nworkers): + t = threading.Thread(target=worker, args=(n, sema,)) + t.daemon=True + t.start() + +print('About to release first worker') +time.sleep(5) +sema.release() +time.sleep(1) +print('About to release second worker') +time.sleep(5) +sema.release() +time.sleep(1) +print('Goodbye') diff --git a/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py b/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py new file mode 100644 index 0000000..fa57d04 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_lock_critical_sections/example1.py @@ -0,0 +1,45 @@ +import threading + +class SharedCounter: + ''' + A counter object that can be shared by multiple threads. + ''' + def __init__(self, initial_value = 0): + self._value = initial_value + self._value_lock = threading.Lock() + + def incr(self,delta=1): + ''' + Increment the counter with locking + ''' + with self._value_lock: + self._value += delta + + def decr(self,delta=1): + ''' + Decrement the counter with locking + ''' + with self._value_lock: + self._value -= delta + +def test(c): + for n in range(1000000): + c.incr() + for n in range(1000000): + c.decr() + +if __name__ == '__main__': + c = SharedCounter() + t1 = threading.Thread(target=test, args=(c,)) + t2 = threading.Thread(target=test, args=(c,)) + t3 = threading.Thread(target=test, args=(c,)) + t1.start() + t2.start() + t3.start() + print('Running test') + t1.join() + t2.join() + t3.join() + + assert c._value == 0 + print('Looks good!') diff --git a/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py b/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py new file mode 100644 index 0000000..a67b950 --- /dev/null +++ b/chef/cookbooks/python/src/12/how_to_start_and_stop_threads/example.py @@ -0,0 +1,26 @@ +from threading import Thread +import time + +class CountdownTask: + def __init__(self): + self._running = True + + def terminate(self): + self._running = False + + def run(self, n): + while self._running and n > 0: + print("T-minus", n) + n -= 1 + time.sleep(5) + +c = CountdownTask() +t = Thread(target=c.run, args=(10,)) +t.start() + +time.sleep(20) +print('About to terminate') +c.terminate() +t.join() +print('Terminated') + diff --git a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py new file mode 100644 index 0000000..0f58cd7 --- /dev/null +++ b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange1.py @@ -0,0 +1,46 @@ +from collections import defaultdict + +class Exchange: + def __init__(self): + self._subscribers = set() + + def attach(self, task): + self._subscribers.add(task) + + def detach(self, task): + self._subscribers.remove(task) + + def send(self, msg): + for subscriber in self._subscribers: + subscriber.send(msg) + +# Dictionary of all created exchanges +_exchanges = defaultdict(Exchange) + +# Return the Exchange instance associated with a given name +def get_exchange(name): + return _exchanges[name] + +if __name__ == '__main__': + # Example task (just for testing) + class Task: + def __init__(self, name): + self.name = name + def send(self, msg): + print('{} got: {!r}'.format(self.name, msg)) + + task_a = Task('A') + task_b = Task('B') + + exc = get_exchange('spam') + exc.attach(task_a) + exc.attach(task_b) + exc.send('msg1') + exc.send('msg2') + + exc.detach(task_a) + exc.detach(task_b) + exc.send('msg3') + + + diff --git a/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py new file mode 100644 index 0000000..e530237 --- /dev/null +++ b/chef/cookbooks/python/src/12/implementing_publish_subscribe_messaging/exchange2.py @@ -0,0 +1,55 @@ +from contextlib import contextmanager +from collections import defaultdict + +class Exchange: + def __init__(self): + self._subscribers = set() + + def attach(self, task): + self._subscribers.add(task) + + def detach(self, task): + self._subscribers.remove(task) + + @contextmanager + def subscribe(self, *tasks): + for task in tasks: + self.attach(task) + try: + yield + finally: + for task in tasks: + self.detach(task) + + def send(self, msg): + for subscriber in self._subscribers: + subscriber.send(msg) + + +# Dictionary of all created exchanges +_exchanges = defaultdict(Exchange) + +# Return the Exchange instance associated with a given name +def get_exchange(name): + return _exchanges[name] + +# Example of using the subscribe() method +if __name__ == '__main__': + # Example task (just for testing) + class Task: + def __init__(self, name): + self.name = name + def send(self, msg): + print('{} got: {!r}'.format(self.name, msg)) + + task_a = Task('A') + task_b = Task('B') + + exc = get_exchange('spam') + with exc.subscribe(task_a, task_b): + exc.send('msg1') + exc.send('msg2') + + exc.send('msg3') + + diff --git a/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py b/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py new file mode 100644 index 0000000..62c1f86 --- /dev/null +++ b/chef/cookbooks/python/src/12/launching_a_daemon_process_on_unix/daemon.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# daemon.py + +import os +import sys +import atexit +import signal + +def daemonize(pidfile, *, stdin='/dev/null', + stdout='/dev/null', + stderr='/dev/null'): + + if os.path.exists(pidfile): + raise RuntimeError('Already running') + + # First fork (detaches from parent) + try: + if os.fork() > 0: + raise SystemExit(0) # Parent exit + except OSError as e: + raise RuntimeError('fork #1 failed.') + + os.chdir('/') + os.umask(0) + os.setsid() + # Second fork (relinquish session leadership) + try: + if os.fork() > 0: + raise SystemExit(0) + except OSError as e: + raise RuntimeError('fork #2 failed.') + + # Flush I/O buffers + sys.stdout.flush() + sys.stderr.flush() + + # Replace file descriptors for stdin, stdout, and stderr + with open(stdin, 'rb', 0) as f: + os.dup2(f.fileno(), sys.stdin.fileno()) + with open(stdout, 'ab', 0) as f: + os.dup2(f.fileno(), sys.stdout.fileno()) + with open(stderr, 'ab', 0) as f: + os.dup2(f.fileno(), sys.stderr.fileno()) + + # Write the PID file + with open(pidfile,'w') as f: + print(os.getpid(),file=f) + + # Arrange to have the PID file removed on exit/signal + atexit.register(lambda: os.remove(pidfile)) + + # Signal handler for termination (required) + def sigterm_handler(signo, frame): + raise SystemExit(1) + + signal.signal(signal.SIGTERM, sigterm_handler) + +def main(): + import time + sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid())) + while True: + sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime())) + time.sleep(10) + +if __name__ == '__main__': + PIDFILE = '/tmp/daemon.pid' + + if len(sys.argv) != 2: + print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr) + raise SystemExit(1) + + if sys.argv[1] == 'start': + try: + daemonize(PIDFILE, + stdout='/tmp/daemon.log', + stderr='/tmp/dameon.log') + except RuntimeError as e: + print(e, file=sys.stderr) + raise SystemExit(1) + + main() + + elif sys.argv[1] == 'stop': + if os.path.exists(PIDFILE): + with open(PIDFILE) as f: + os.kill(int(f.read()), signal.SIGTERM) + else: + print('Not running', file=sys.stderr) + raise SystemExit(1) + + else: + print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr) + raise SystemExit(1) + diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py new file mode 100644 index 0000000..394a263 --- /dev/null +++ b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/deadlock.py @@ -0,0 +1,28 @@ +import threading +from contextlib import contextmanager + +# Thread-local state to stored information on locks already acquired +_local = threading.local() + +@contextmanager +def acquire(*locks): + # Sort locks by object identifier + locks = sorted(locks, key=lambda x: id(x)) + + # Make sure lock order of previously acquired locks is not violated + acquired = getattr(_local, 'acquired',[]) + if acquired and max(id(lock) for lock in acquired) >= id(locks[0]): + raise RuntimeError('Lock Order Violation') + + # Acquire all of the locks + acquired.extend(locks) + _local.acquired = acquired + try: + for lock in locks: + lock.acquire() + yield + finally: + # Release locks in reverse order of acquisition + for lock in reversed(locks): + lock.release() + del acquired[-len(locks):] diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py new file mode 100644 index 0000000..dedbbf7 --- /dev/null +++ b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example1.py @@ -0,0 +1,29 @@ +import threading +from deadlock import acquire + +x_lock = threading.Lock() +y_lock = threading.Lock() + +def thread_1(): + while True: + with acquire(x_lock, y_lock): + print("Thread-1") + +def thread_2(): + while True: + with acquire(y_lock, x_lock): + print("Thread-2") + +input('This program runs forever. Press [return] to start, Ctrl-C to exit') + +t1 = threading.Thread(target=thread_1) +t1.daemon = True +t1.start() + +t2 = threading.Thread(target=thread_2) +t2.daemon = True +t2.start() + +import time +while True: + time.sleep(1) diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py new file mode 100644 index 0000000..adfd222 --- /dev/null +++ b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example2.py @@ -0,0 +1,34 @@ +import threading +import time +from deadlock import acquire + + +x_lock = threading.Lock() +y_lock = threading.Lock() + +def thread_1(): + while True: + with acquire(x_lock): + with acquire(y_lock): + print("Thread-1") + time.sleep(1) + +def thread_2(): + while True: + with acquire(y_lock): + with acquire(x_lock): + print("Thread-2") + time.sleep(1) + +input('This program crashes with an exception. Press [return] to start') + +t1 = threading.Thread(target=thread_1) +t1.daemon = True +t1.start() + +t2 = threading.Thread(target=thread_2) +t2.daemon = True +t2.start() + +time.sleep(5) + diff --git a/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py new file mode 100644 index 0000000..12e2815 --- /dev/null +++ b/chef/cookbooks/python/src/12/locking_with_deadlock_avoidance/example3.py @@ -0,0 +1,26 @@ +import threading +from deadlock import acquire + +# The philosopher thread +def philosopher(left, right): + while True: + with acquire(left,right): + print(threading.currentThread(), 'eating') + +# The chopsticks (represented by locks) +NSTICKS = 5 +chopsticks = [threading.Lock() for n in range(NSTICKS)] + +# Create all of the philosophers +for n in range(NSTICKS): + t = threading.Thread(target=philosopher, + args=(chopsticks[n],chopsticks[(n+1) % NSTICKS])) + t.daemon = True + t.start() + +import time +while True: + time.sleep(1) + + + diff --git a/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py b/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py new file mode 100644 index 0000000..47edd1f --- /dev/null +++ b/chef/cookbooks/python/src/12/polling_multiple_thread_queues/pqueue.py @@ -0,0 +1,63 @@ +import queue +import socket +import os + +class PollableQueue(queue.Queue): + def __init__(self): + super().__init__() + # Create a pair of connected sockets + if os.name == 'posix': + self._putsocket, self._getsocket = socket.socketpair() + else: + # Compatibility on non-POSIX systems + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.bind(('127.0.0.1', 0)) + server.listen(1) + self._putsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._putsocket.connect(server.getsockname()) + self._getsocket, _ = server.accept() + server.close() + + def fileno(self): + return self._getsocket.fileno() + + def put(self, item): + super().put(item) + self._putsocket.send(b'x') + + def get(self): + self._getsocket.recv(1) + return super().get() + +# Example code that performs polling: + +if __name__ == '__main__': + import select + import threading + import time + + def consumer(queues): + ''' + Consumer that reads data on multiple queues simultaneously + ''' + while True: + can_read, _, _ = select.select(queues,[],[]) + for r in can_read: + item = r.get() + print('Got:', item) + + q1 = PollableQueue() + q2 = PollableQueue() + q3 = PollableQueue() + t = threading.Thread(target=consumer, args=([q1,q2,q3],)) + t.daemon = True + t.start() + + # Feed data to the queues + q1.put(1) + q2.put(10) + q3.put('hello') + q2.put(15) + + # Give thread time to run + time.sleep(1) diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py new file mode 100644 index 0000000..1dcd60c --- /dev/null +++ b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots.py @@ -0,0 +1,37 @@ +# findrobots.py + +import gzip +import io +import glob + +def find_robots(filename): + ''' + Find all of the hosts that access robots.txt in a single log file + ''' + robots = set() + with gzip.open(filename) as f: + for line in io.TextIOWrapper(f,encoding='ascii'): + fields = line.split() + if fields[6] == '/robots.txt': + robots.add(fields[0]) + return robots + +def find_all_robots(logdir): + ''' + Find all hosts across and entire sequence of files + ''' + files = glob.glob(logdir+"/*.log.gz") + all_robots = set() + for robots in map(find_robots, files): + all_robots.update(robots) + return all_robots + +if __name__ == '__main__': + import time + start = time.time() + robots = find_all_robots("logs") + end = time.time() + for ipaddr in robots: + print(ipaddr) + print('Took {:f} seconds'.format(end-start)) + diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py new file mode 100644 index 0000000..3e7a588 --- /dev/null +++ b/chef/cookbooks/python/src/12/simple_parallel_programming/findrobots_par.py @@ -0,0 +1,38 @@ +# findrobots.py + +import gzip +import io +import glob +from concurrent import futures + +def find_robots(filename): + ''' + Find all of the hosts that access robots.txt in a single log file + ''' + robots = set() + with gzip.open(filename) as f: + for line in io.TextIOWrapper(f,encoding='ascii'): + fields = line.split() + if fields[6] == '/robots.txt': + robots.add(fields[0]) + return robots + +def find_all_robots(logdir): + ''' + Find all hosts across and entire sequence of files + ''' + files = glob.glob(logdir+"/*.log.gz") + all_robots = set() + with futures.ProcessPoolExecutor() as pool: + for robots in pool.map(find_robots, files): + all_robots.update(robots) + return all_robots + +if __name__ == '__main__': + import time + start = time.time() + robots = find_all_robots("logs") + end = time.time() + for ipaddr in robots: + print(ipaddr) + print('Took {:f} seconds'.format(end-start)) diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz new file mode 100644 index 0000000..8dd67b8 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121217.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121218.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121218.log.gz new file mode 100644 index 0000000..cd0be6c Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121218.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121219.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121219.log.gz new file mode 100644 index 0000000..ad34479 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121219.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz new file mode 100644 index 0000000..3578581 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121220.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz new file mode 100644 index 0000000..72dbf6c Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121221.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz new file mode 100644 index 0000000..15f3ed0 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121222.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz new file mode 100644 index 0000000..6ffc86d Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121223.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz new file mode 100644 index 0000000..bf68125 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121224.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz new file mode 100644 index 0000000..134c58d Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121225.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121226.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121226.log.gz new file mode 100644 index 0000000..0b7e20b Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121226.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz new file mode 100644 index 0000000..07df607 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121227.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz new file mode 100644 index 0000000..41c95cd Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121228.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz new file mode 100644 index 0000000..59d8724 Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121229.log.gz differ diff --git a/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121230.log.gz b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121230.log.gz new file mode 100644 index 0000000..952347b Binary files /dev/null and b/chef/cookbooks/python/src/12/simple_parallel_programming/logs/20121230.log.gz differ diff --git a/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py b/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py new file mode 100644 index 0000000..42523b5 --- /dev/null +++ b/chef/cookbooks/python/src/12/storing_thread_specific_state/example1.py @@ -0,0 +1,45 @@ +from socket import socket, AF_INET, SOCK_STREAM +import threading + +class LazyConnection: + def __init__(self, address, family=AF_INET, type=SOCK_STREAM): + self.address = address + self.family = AF_INET + self.type = SOCK_STREAM + self.local = threading.local() + + def __enter__(self): + if hasattr(self.local, 'sock'): + raise RuntimeError('Already connected') + self.local.sock = socket(self.family, self.type) + self.local.sock.connect(self.address) + return self.local.sock + + def __exit__(self, exc_ty, exc_val, tb): + self.local.sock.close() + del self.local.sock + +def test(conn): + from functools import partial + + # Connection closed + with conn as s: + # conn.__enter__() executes: connection open + s.send(b'GET /index.html HTTP/1.0\r\n') + s.send(b'Host: www.python.org\r\n') + s.send(b'\r\n') + resp = b''.join(iter(partial(s.recv, 8192), b'')) + # conn.__exit__() executes: connection closed + + print('Got {} bytes'.format(len(resp))) + +if __name__ == '__main__': + conn = LazyConnection(('www.python.org', 80)) + + t1 = threading.Thread(target=test, args=(conn,)) + t2 = threading.Thread(target=test, args=(conn,)) + t1.start() + t2.start() + t1.join() + t2.join() + diff --git a/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py b/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py new file mode 100644 index 0000000..12e0640 --- /dev/null +++ b/chef/cookbooks/python/src/12/storing_thread_specific_state/example2.py @@ -0,0 +1,60 @@ +from socket import socket, AF_INET, SOCK_STREAM +import threading + +class LazyConnection: + def __init__(self, address, family=AF_INET, type=SOCK_STREAM): + self.address = address + self.family = AF_INET + self.type = SOCK_STREAM + self.local = threading.local() + + def __enter__(self): + sock = socket(self.family, self.type) + sock.connect(self.address) + if not hasattr(self.local, 'connections'): + self.local.connections = [] + self.local.connections.append(sock) + return sock + + def __exit__(self, exc_ty, exc_val, tb): + self.local.connections.pop().close() + +def test(conn): + # Example use + from functools import partial + + with conn as s: + s.send(b'GET /index.html HTTP/1.0\r\n') + s.send(b'Host: www.python.org\r\n') + s.send(b'\r\n') + resp = b''.join(iter(partial(s.recv, 8192), b'')) + + print('Got {} bytes'.format(len(resp))) + + with conn as s1, conn as s2: + s1.send(b'GET /downloads HTTP/1.0\r\n') + s2.send(b'GET /index.html HTTP/1.0\r\n') + s1.send(b'Host: www.python.org\r\n') + s2.send(b'Host: www.python.org\r\n') + s1.send(b'\r\n') + s2.send(b'\r\n') + resp1 = b''.join(iter(partial(s1.recv, 8192), b'')) + resp2 = b''.join(iter(partial(s2.recv, 8192), b'')) + + print('resp1 got {} bytes'.format(len(resp1))) + print('resp2 got {} bytes'.format(len(resp2))) + +if __name__ == '__main__': + + conn = LazyConnection(('www.python.org', 80)) + t1 = threading.Thread(target=test, args=(conn,)) + t2 = threading.Thread(target=test, args=(conn,)) + t3 = threading.Thread(target=test, args=(conn,)) + t1.start() + t2.start() + t3.start() + t1.join() + t2.join() + t3.join() + + diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py new file mode 100644 index 0000000..8f2e57f --- /dev/null +++ b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/actorsched.py @@ -0,0 +1,59 @@ +from collections import deque + +class ActorScheduler: + def __init__(self): + self._actors = { } # Mapping of names to actors + self._msg_queue = deque() # Message queue + + def new_actor(self, name, actor): + ''' + Admit a newly started actor to the scheduler and give it a name + ''' + self._msg_queue.append((actor,None)) + self._actors[name] = actor + + def send(self, name, msg): + ''' + Send a message to a named actor + ''' + actor = self._actors.get(name) + if actor: + self._msg_queue.append((actor,msg)) + + def run(self): + ''' + Run as long as there are pending messages. + ''' + while self._msg_queue: + actor, msg = self._msg_queue.popleft() + try: + actor.send(msg) + except StopIteration: + pass + +# Example use +if __name__ == '__main__': + def printer(): + while True: + msg = yield + print('Got:', msg) + + def counter(sched): + while True: + # Receive the current count + n = yield + if n == 0: + break + # Send to the printer task + sched.send('printer', n) + # Send the next count to the counter task (recursive) + sched.send('counter', n-1) + + sched = ActorScheduler() + # Create the initial actors + sched.new_actor('printer', printer()) + sched.new_actor('counter', counter(sched)) + + # Send an initial message to the counter to initiate + sched.send('counter', 10000) + sched.run() diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py new file mode 100644 index 0000000..b6dba71 --- /dev/null +++ b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/netsched.py @@ -0,0 +1,159 @@ +from collections import deque +from select import select + +# This class represents a generic yield event in the scheduler +class YieldEvent: + def handle_yield(self, sched, task): + pass + def handle_resume(self, sched, task): + pass + +# Task Scheduler +class Scheduler: + def __init__(self): + self._numtasks = 0 # Total num of tasks + self._ready = deque() # Tasks ready to run + self._read_waiting = {} # Tasks waiting to read + self._write_waiting = {} # Tasks waiting to write + + # Poll for I/O events and restart waiting tasks + def _iopoll(self): + rset,wset,eset = select(self._read_waiting, + self._write_waiting,[]) + for r in rset: + evt, task = self._read_waiting.pop(r) + evt.handle_resume(self, task) + for w in wset: + evt, task = self._write_waiting.pop(w) + evt.handle_resume(self, task) + + def new(self,task): + ''' + Add a newly started task to the scheduler + ''' + self._ready.append((task, None)) + self._numtasks += 1 + + def add_ready(self, task, msg=None): + ''' + Append an already started task to the ready queue. + msg is what to send into the task when it resumes. + ''' + self._ready.append((task, msg)) + + # Add a task to the reading set + def _read_wait(self, fileno, evt, task): + self._read_waiting[fileno] = (evt, task) + + # Add a task to the write set + def _write_wait(self, fileno, evt, task): + self._write_waiting[fileno] = (evt, task) + + def run(self): + ''' + Run the task scheduler until there are no tasks + ''' + while self._numtasks: + if not self._ready: + self._iopoll() + task, msg = self._ready.popleft() + try: + # Run the coroutine to the next yield + r = task.send(msg) + if isinstance(r, YieldEvent): + r.handle_yield(self, task) + else: + raise RuntimeError('unrecognized yield event') + except StopIteration: + self._numtasks -= 1 + +# Example implementation of coroutine based socket I/O +class ReadSocket(YieldEvent): + def __init__(self, sock, nbytes): + self.sock = sock + self.nbytes = nbytes + def handle_yield(self, sched, task): + sched._read_wait(self.sock.fileno(), self, task) + def handle_resume(self, sched, task): + data = self.sock.recv(self.nbytes) + sched.add_ready(task, data) + +class WriteSocket(YieldEvent): + def __init__(self, sock, data): + self.sock = sock + self.data = data + def handle_yield(self, sched, task): + sched._write_wait(self.sock.fileno(), self, task) + def handle_resume(self, sched, task): + nsent = self.sock.send(self.data) + sched.add_ready(task, nsent) + +class AcceptSocket(YieldEvent): + def __init__(self, sock): + self.sock = sock + def handle_yield(self, sched, task): + sched._read_wait(self.sock.fileno(), self, task) + def handle_resume(self, sched, task): + r = self.sock.accept() + sched.add_ready(task, r) + +# Wrapper around a socket object for use with yield +class Socket(object): + def __init__(self, sock): + self._sock = sock + def recv(self, maxbytes): + return ReadSocket(self._sock, maxbytes) + def send(self, data): + return WriteSocket(self._sock, data) + def accept(self): + return AcceptSocket(self._sock) + def __getattr__(self, name): + return getattr(self._sock, name) + +if __name__ == '__main__': + from socket import socket, AF_INET, SOCK_STREAM + import time + + # Example of a function involving generators. This should + # be called using line = yield from readline(sock) + def readline(sock): + chars = [] + while True: + c = yield sock.recv(1) + if not c: + break + chars.append(c) + if c == b'\n': + break + return b''.join(chars) + + # Echo server using generators + class EchoServer: + def __init__(self,addr,sched): + self.sched = sched + sched.new(self.server_loop(addr)) + + def server_loop(self,addr): + s = Socket(socket(AF_INET,SOCK_STREAM)) + s.bind(addr) + s.listen(5) + while True: + c,a = yield s.accept() + print('Got connection from ', a) + self.sched.new(self.client_handler(Socket(c))) + + def client_handler(self,client): + while True: + line = yield from readline(client) + if not line: + break + line = b'GOT:' + line + while line: + nsent = yield client.send(line) + line = line[nsent:] + client.close() + print('Client closed') + + sched = Scheduler() + EchoServer(('',16000),sched) + sched.run() diff --git a/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py new file mode 100644 index 0000000..ed178c1 --- /dev/null +++ b/chef/cookbooks/python/src/12/using_generators_as_an_alternative_to_threads/simple.py @@ -0,0 +1,49 @@ +# A very simple example of a coroutine/generator scheduler + +# Two simple generator functions +def countdown(n): + while n > 0: + print("T-minus", n) + yield + n -= 1 + print("Blastoff!") + +def countup(n): + x = 0 + while x < n: + print("Counting up", x) + yield + x += 1 + +from collections import deque + +class TaskScheduler: + def __init__(self): + self._task_queue = deque() + + def new_task(self, task): + ''' + Admit a newly started task to the scheduler + ''' + self._task_queue.append(task) + + def run(self): + ''' + Run until there are no more tasks + ''' + while self._task_queue: + task = self._task_queue.popleft() + try: + # Run until the next yield statement + next(task) + self._task_queue.append(task) + except StopIteration: + # Generator is no longer executing + pass + +# Example use +sched = TaskScheduler() +sched.new_task(countdown(10)) +sched.new_task(countdown(5)) +sched.new_task(countup(15)) +sched.run() diff --git a/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py b/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py new file mode 100644 index 0000000..96f754e --- /dev/null +++ b/chef/cookbooks/python/src/13/adding_logging_to_libraries/somelib.py @@ -0,0 +1,10 @@ +# somelib.py + +import logging +log = logging.getLogger(__name__) +log.addHandler(logging.NullHandler()) + +# Example function (for testing) +def func(): + log.critical("A Critical Error!") + log.debug("A debug message") diff --git a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py new file mode 100644 index 0000000..781843d --- /dev/null +++ b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example1.py @@ -0,0 +1,9 @@ +import subprocess +try: + out_bytes = subprocess.check_output(['netstat', '-a']) + out_text = out_bytes.decode('utf-8') + print(out_text) +except subprocess.CalledProcessError as e: + print('It did not work. Reason:', e) + print('Exitcode:', e.returncode) + diff --git a/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py new file mode 100644 index 0000000..5ced9ad --- /dev/null +++ b/chef/cookbooks/python/src/13/executing_an_external_command_and_getting_its_output/example2.py @@ -0,0 +1,20 @@ +import subprocess + +# Some text to send +text = b''' +hello world +this is a test +goodbye +''' + +# Launch a command with pipes +p = subprocess.Popen(['wc'], + stdout = subprocess.PIPE, + stdin = subprocess.PIPE) + +# Send the data and get the output +stdout, stderr = p.communicate(text) + +text = stdout.decode('utf-8') +print(text) + diff --git a/chef/cookbooks/python/src/13/finding_files/modified_within.py b/chef/cookbooks/python/src/13/finding_files/modified_within.py new file mode 100644 index 0000000..de0ff24 --- /dev/null +++ b/chef/cookbooks/python/src/13/finding_files/modified_within.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3.3 + +import os +import time + +def modified_within(top, seconds): + now = time.time() + for path, dirs, files in os.walk(top): + for name in files: + fullpath = os.path.join(path, name) + if os.path.exists(fullpath): + mtime = os.path.getmtime(fullpath) + if mtime > (now - seconds): + print(fullpath) + + +if __name__ == '__main__': + import sys + if len(sys.argv) != 3: + print('Usage: {} dir seconds'.format(sys.argv[0])) + raise SystemExit(1) + + modified_within(sys.argv[1], float(sys.argv[2])) + + + + diff --git a/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py b/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py new file mode 100644 index 0000000..4966063 --- /dev/null +++ b/chef/cookbooks/python/src/13/generating_a_range_of_ip_addresses_from_a_cidr_address/example.py @@ -0,0 +1,26 @@ +from socket import AF_INET, AF_INET6, inet_pton, inet_ntop + +def cidr_range(cidr_address): + family = AF_INET6 if ':' in cidr_address else AF_INET + address, maskstr = cidr_address.split('/') + maskbits = int(maskstr) + + # Parse the supplied address into bytes + addr_bytes = inet_pton(family, address) + + # Calculate number of address bytes and mask bits + addr_len = len(addr_bytes) + numaddrs = 2**(addr_len*8 - maskbits) + mask = -numaddrs + + # Generate addresses + addr = int.from_bytes(addr_bytes, 'big') & mask + for n in range(numaddrs): + yield inet_ntop(family, (addr+n).to_bytes(addr_len, 'big')) + +if __name__ == '__main__': + for a in cidr_range('123.45.67.89/27'): + print(a) + + for a in cidr_range('12:3456:78:90ab:cd:ef01:23:34/125'): + print(a) diff --git a/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py b/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py new file mode 100644 index 0000000..a6e78e9 --- /dev/null +++ b/chef/cookbooks/python/src/13/getting_the_terminal_size/example.py @@ -0,0 +1,4 @@ +import os +sz = os.get_terminal_size() +print(sz.columns, 'columns') +print(sz.lines, 'lines') diff --git a/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py b/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py new file mode 100644 index 0000000..756f473 --- /dev/null +++ b/chef/cookbooks/python/src/13/making_a_stopwatch/stopwatch.py @@ -0,0 +1,50 @@ +import time + +class Timer: + def __init__(self, func=time.perf_counter): + self.elapsed = 0.0 + self._func = func + self._start = None + + def start(self): + if self._start is not None: + raise RuntimeError('Already started') + self._start = self._func() + + def end(self): + if self._start is None: + raise RuntimeError('Not started') + end = self._func() + self.elapsed += end - self._start + self._start = None + + def reset(self): + self.elapsed = 0.0 + + @property + def running(self): + return self._start is not None + + def __enter__(self): + self.start() + return self + + def __exit__(self, *args): + self.end() + + +if __name__ == '__main__': + def countdown(n): + while n > 0: + n -= 1 + + t = Timer() + t.start() + countdown(1000000) + t.end() + print(t.elapsed) + + with t: + countdown(1000000) + print(t.elapsed) + diff --git a/chef/cookbooks/python/src/13/parsing_command_line_options/search.py b/chef/cookbooks/python/src/13/parsing_command_line_options/search.py new file mode 100644 index 0000000..891fd46 --- /dev/null +++ b/chef/cookbooks/python/src/13/parsing_command_line_options/search.py @@ -0,0 +1,32 @@ +# search.py +''' +Hypothetical command line tool for searching a collection of +files for one or more text patterns. +''' +import argparse +parser = argparse.ArgumentParser(description='Search some files') + +parser.add_argument(dest='filenames',metavar='filename', nargs='*') + +parser.add_argument('-p', '--pat',metavar='pattern', required=True, + dest='patterns', action='append', + help='text pattern to search for') + +parser.add_argument('-v', dest='verbose', action='store_true', + help='verbose mode') + +parser.add_argument('-o', dest='outfile', action='store', + help='output file') + +parser.add_argument('--speed', dest='speed', action='store', + choices={'slow','fast'}, default='slow', + help='search speed') + +args = parser.parse_args() + +# Output the collected arguments +print(args.filenames) +print(args.patterns) +print(args.verbose) +print(args.outfile) +print(args.speed) diff --git a/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py b/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py new file mode 100644 index 0000000..66f572e --- /dev/null +++ b/chef/cookbooks/python/src/13/prompting_for_a_password_at_runtime/example.py @@ -0,0 +1,7 @@ +import getpass + +user = getpass.getuser() +passwd = getpass.getpass() + +print('User:', user) +print('Passwd:', passwd) diff --git a/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py b/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py new file mode 100644 index 0000000..1dd347e --- /dev/null +++ b/chef/cookbooks/python/src/13/putting_limits_on_memory_and_cpu_usage/example.py @@ -0,0 +1,18 @@ +import signal +import resource +import os + +def time_exceeded(signo, frame): + print("Time's up!") + raise SystemExit(1) + +def set_max_runtime(seconds): + # Install the signal handler and set a resource limit + soft, hard = resource.getrlimit(resource.RLIMIT_CPU) + resource.setrlimit(resource.RLIMIT_CPU, (seconds, hard)) + signal.signal(signal.SIGXCPU, time_exceeded) + +if __name__ == '__main__': + set_max_runtime(15) + while True: + pass diff --git a/chef/cookbooks/python/src/13/reading_configuration_files/config.ini b/chef/cookbooks/python/src/13/reading_configuration_files/config.ini new file mode 100644 index 0000000..f588545 --- /dev/null +++ b/chef/cookbooks/python/src/13/reading_configuration_files/config.ini @@ -0,0 +1,24 @@ + + ; config.ini + ; Sample configuration file + + [installation] + library=%(prefix)s/lib + include=%(prefix)s/include + bin=%(prefix)s/bin + prefix=/usr/local + + # Setting related to debug configuration + [debug] + log_errors=true + show_warnings=False + + [server] + port: 8080 + nworkers: 32 + pid-file=/tmp/spam.pid + root=/www/root + signature: + ================================= + Brought to by the Python Cookbook + ================================= diff --git a/chef/cookbooks/python/src/13/reading_configuration_files/example1.py b/chef/cookbooks/python/src/13/reading_configuration_files/example1.py new file mode 100644 index 0000000..8922073 --- /dev/null +++ b/chef/cookbooks/python/src/13/reading_configuration_files/example1.py @@ -0,0 +1,9 @@ +from configparser import ConfigParser +cfg = ConfigParser() +cfg.read('config.ini') +print('sections:', cfg.sections()) +print('installation:library', cfg.get('installation','library')) +print('debug:log_errors', cfg.getboolean('debug','log_errors')) +print('server:port', cfg.getint('server','port')) +print('server:nworkers', cfg.getint('server','nworkers')) +print('server:signature', cfg.get('server','signature')) diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py new file mode 100644 index 0000000..b584eab --- /dev/null +++ b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example1.py @@ -0,0 +1,24 @@ +import logging + +def main(): + # Configure the logging system + logging.basicConfig( + filename='app.log', + level=logging.ERROR + ) + + # Variables (to make the calls that follow work) + hostname = 'www.python.org' + item = 'spam' + filename = 'data.csv' + mode = 'r' + + # Example logging calls (insert into your program) + logging.critical('Host %s unknown', hostname) + logging.error("Couldn't find %r", item) + logging.warning('Feature is deprecated') + logging.info('Opening file %r, mode=%r', filename, mode) + logging.debug('Got here') + +if __name__ == '__main__': + main() diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py new file mode 100644 index 0000000..61059dc --- /dev/null +++ b/chef/cookbooks/python/src/13/simple_logging_for_scripts/example2.py @@ -0,0 +1,22 @@ +import logging +import logging.config + +def main(): + # Configure the logging system + logging.config.fileConfig('logconfig.ini') + + # Variables (to make the calls that follow work) + hostname = 'www.python.org' + item = 'spam' + filename = 'data.csv' + mode = 'r' + + # Example logging calls (insert into your program) + logging.critical('Host %s unknown', hostname) + logging.error("Couldn't find %r", item) + logging.warning('Feature is deprecated') + logging.info('Opening file %r, mode=%r', filename, mode) + logging.debug('Got here') + +if __name__ == '__main__': + main() diff --git a/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini b/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini new file mode 100644 index 0000000..37b7d36 --- /dev/null +++ b/chef/cookbooks/python/src/13/simple_logging_for_scripts/logconfig.ini @@ -0,0 +1,21 @@ +[loggers] +keys=root + +[handlers] +keys=defaultHandler + +[formatters] +keys=defaultFormatter + +[logger_root] +level=INFO +handlers=defaultHandler +qualname=root + +[handler_defaultHandler] +class=FileHandler +formatter=defaultFormatter +args=('app.log', 'a') + +[formatter_defaultFormatter] +format=%(levelname)s:%(name)s:%(message)s diff --git a/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py b/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py new file mode 100644 index 0000000..e05221f --- /dev/null +++ b/chef/cookbooks/python/src/14/logging_test_output_to_a_file/test.py @@ -0,0 +1,36 @@ +import unittest + +# A simple function to illustrate +def parse_int(s): + return int(s) + +class TestConversion(unittest.TestCase): + # Testing that an exception gets raised + def test_bad_int(self): + self.assertRaises(ValueError, parse_int, "N/A") + + # Testing an exception plus regex on exception message + def test_bad_int_msg(self): + self.assertRaisesRegex(ValueError, 'invalid literal .*', parse_int, 'N/A') + +# Example of testing an exception along with inspection of exception instance +import errno + +class TestIO(unittest.TestCase): + def test_file_not_found(self): + try: + f = open('/file/not/found') + except IOError as e: + self.assertEqual(e.errno, errno.ENOENT) + else: + self.fail("IOError not raised") + +import sys +def main(out=sys.stderr, verbosity=2): + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(sys.modules[__name__]) + unittest.TextTestRunner(out, verbosity=verbosity).run(suite) + +if __name__ == '__main__': + with open('testing.out', 'w') as f: + main(f) diff --git a/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py b/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py new file mode 100644 index 0000000..045aeac --- /dev/null +++ b/chef/cookbooks/python/src/14/make_your_programs_run_faster/example.py @@ -0,0 +1,35 @@ +import time +def test(func): + start = time.time() + nums = range(1000000) + for n in range(100): + r = func(nums) + end = time.time() + print(func.__name__, ':', end-start) + +import math +def compute_roots_1(nums): + result = [] + for n in nums: + result.append(math.sqrt(n)) + return result + +from math import sqrt +def compute_roots_2(nums): + result = [] + result_append = result.append + for n in nums: + result_append(sqrt(n)) + return result + +def compute_roots_3(nums): + sqrt = math.sqrt + result = [] + result_append = result.append + for n in nums: + result_append(sqrt(n)) + return result + +tests = [compute_roots_1, compute_roots_2, compute_roots_3] +for func in tests: + test(func) diff --git a/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py b/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py new file mode 100644 index 0000000..5e82448 --- /dev/null +++ b/chef/cookbooks/python/src/14/profiling_and_timing_your_program/timethis.py @@ -0,0 +1,23 @@ +# timethis.py + +import time +from functools import wraps + +def timethis(func): + @wraps(func) + def wrapper(*args, **kwargs): + start = time.perf_counter() + r = func(*args, **kwargs) + end = time.perf_counter() + print('{}.{} : {}'.format(func.__module__, func.__name__, end-start)) + return r + return wrapper + +if __name__ == '__main__': + @timethis + def countdown(n): + while n > 0: + n -= 1 + + + countdown(10000000) diff --git a/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py b/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py new file mode 100644 index 0000000..3e8e6a6 --- /dev/null +++ b/chef/cookbooks/python/src/14/raising_an_exception_in_response_to_another_exception/example.py @@ -0,0 +1,50 @@ +# Different styles of raising chained exceptions + +# Example 1: Explicit chaining. Use this whenever your +# intent is to raise a new exception in response to another + +def example1(): + try: + int('N/A') + except ValueError as e: + raise RuntimeError('A parsing error occurred') from e + +# Example 2: Implicit chaining. This occurs if there's an +# unexpected exception in the except block. + +def example2(): + try: + int('N/A') + except ValueError as e: + print('It failed. Reason:', err) # Intentional error + +# Example 3: Discarding the previous exception +def example3(): + try: + int('N/A') + except ValueError as e: + raise RuntimeError('A parsing error occurred') from None + +if __name__ == '__main__': + import traceback + print('****** EXPLICIT EXCEPTION CHAINING ******') + try: + example1() + except Exception: + traceback.print_exc() + + print() + print('****** IMPLICIT EXCEPTION CHAINING ******') + try: + example2() + except Exception: + traceback.print_exc() + + print() + print('****** DISCARDED CHAINING *******') + try: + example3() + except Exception: + traceback.print_exc() + + diff --git a/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py b/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py new file mode 100644 index 0000000..aa1cf61 --- /dev/null +++ b/chef/cookbooks/python/src/14/skipping_or_anticipating_test_failures/test.py @@ -0,0 +1,26 @@ +import unittest +import os +import platform + +class Tests(unittest.TestCase): + def test_0(self): + self.assertTrue(True) + + @unittest.skip('skipped test') + def test_1(self): + self.fail("should have failed!") + + @unittest.skipIf(os.name=='posix', 'Not supported on Unix') + def test_2(self): + import winreg + + @unittest.skipUnless(platform.system() == 'Darwin', 'Mac specific test') + def test_3(self): + self.assertTrue(True) + + @unittest.expectedFailure + def test_4(self): + self.assertEqual(2+2, 5) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py b/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py new file mode 100644 index 0000000..53ab362 --- /dev/null +++ b/chef/cookbooks/python/src/14/testing_for_exceptional_conditions_in_unit_tests/test.py @@ -0,0 +1,29 @@ +import unittest + +# A simple function to illustrate +def parse_int(s): + return int(s) + +class TestConversion(unittest.TestCase): + # Testing that an exception gets raised + def test_bad_int(self): + self.assertRaises(ValueError, parse_int, "N/A") + + # Testing an exception plus regex on exception message + def test_bad_int_msg(self): + self.assertRaisesRegex(ValueError, 'invalid literal .*', parse_int, 'N/A') + +# Example of testing an exception along with inspection of exception instance +import errno + +class TestIO(unittest.TestCase): + def test_file_not_found(self): + try: + f = open('/file/not/found') + except IOError as e: + self.assertEqual(e.errno, errno.ENOENT) + else: + self.fail("IOError not raised") + +if __name__ == '__main__': + unittest.main() diff --git a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py new file mode 100644 index 0000000..f0e2547 --- /dev/null +++ b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/mymodule.py @@ -0,0 +1,5 @@ +# mymodule.py + +def urlprint(protocol, host, domain): + url = '{}://{}.{}'.format(protocol, host, domain) + print(url) diff --git a/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py new file mode 100644 index 0000000..ca64b2a --- /dev/null +++ b/chef/cookbooks/python/src/14/testing_output_sent_to_stdout/testmymodule.py @@ -0,0 +1,19 @@ +from io import StringIO +from unittest import TestCase +from unittest.mock import patch +import mymodule + +class TestURLPrint(TestCase): + def test_url_gets_to_stdout(self): + protocol = 'http' + host = 'www' + domain = 'example.com' + expected_url = '{}://{}.{}\n'.format(protocol, host, domain) + + with patch('sys.stdout', new=StringIO()) as fake_out: + mymodule.urlprint(protocol, host, domain) + self.assertEqual(fake_out.getvalue(), expected_url) + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/chef/cookbooks/python/src/15/Makefile b/chef/cookbooks/python/src/15/Makefile new file mode 100644 index 0000000..b0ec491 --- /dev/null +++ b/chef/cookbooks/python/src/15/Makefile @@ -0,0 +1,2 @@ +osx:: + gcc -shared -undefined dynamic_lookup sample.c -o libsample.so diff --git a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py new file mode 100644 index 0000000..a15af47 --- /dev/null +++ b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/example.py @@ -0,0 +1,9 @@ +import sample +print(sample.gcd(35,42)) +print(sample.in_mandel(0,0,500)) +print(sample.in_mandel(2.0,1.0,500)) +print(sample.divide(42,8)) +print(sample.avg([1,2,3])) +p1 = sample.Point(1,2) +p2 = sample.Point(4,5) +print(sample.distance(p1,p2)) diff --git a/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py new file mode 100644 index 0000000..86fd9c2 --- /dev/null +++ b/chef/cookbooks/python/src/15/accessing_c_code_using_ctypes/sample.py @@ -0,0 +1,77 @@ +# sample.py +import ctypes +import os + +# .so file is located in the directory above. See Makefile for +# build instructions +_path = '../libsample.so' +_mod = ctypes.cdll.LoadLibrary(_path) + +# int gcd(int, int) +gcd = _mod.gcd +gcd.argtypes = (ctypes.c_int, ctypes.c_int) +gcd.restype = ctypes.c_int + +# int in_mandel(double, double, int) +in_mandel = _mod.in_mandel +in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int) +in_mandel.restype = ctypes.c_int + +# int divide(int, int, int *) +_divide = _mod.divide +_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int)) +_divide.restype = ctypes.c_int + +def divide(x, y): + rem = ctypes.c_int() + quot = _divide(x,y,rem) + return quot,rem.value + +# void avg(double *, int n) + +# Define a special type for the 'double *' argument +class DoubleArrayType: + def from_param(self, param): + typename = type(param).__name__ + if hasattr(self, 'from_'+typename): + return getattr(self, 'from_'+typename)(param) + elif isinstance(param, ctypes.Array): + return param + else: + raise TypeError("Can't convert %s" % typename) + + # Cast from array.array objects + def from_array(self, param): + if param.typecode != 'd': + raise TypeError('must be an array of doubles') + ptr, _ = param.buffer_info() + return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double)) + + # Cast from lists/tuples + def from_list(self, param): + val = ((ctypes.c_double)*len(param))(*param) + return val + + from_tuple = from_list + + # Cast from a numpy array + def from_ndarray(self, param): + return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) + +DoubleArray = DoubleArrayType() +_avg = _mod.avg +_avg.argtypes = (DoubleArray, ctypes.c_int) +_avg.restype = ctypes.c_double + +def avg(values): + return _avg(values, len(values)) + +# struct Point { } +class Point(ctypes.Structure): + _fields_ = [('x', ctypes.c_double), + ('y', ctypes.c_double)] + +# double distance(Point *, Point *) +distance = _mod.distance +distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point)) +distance.restype = ctypes.c_double diff --git a/chef/cookbooks/python/src/15/calling_python_from_c/Makefile b/chef/cookbooks/python/src/15/calling_python_from_c/Makefile new file mode 100644 index 0000000..5599aa9 --- /dev/null +++ b/chef/cookbooks/python/src/15/calling_python_from_c/Makefile @@ -0,0 +1,3 @@ +all:: + cc -g embed.c -I/usr/local/include/python3.3m \ + -L/usr/local/lib/python3.3/config-3.3m -lpython3.3m diff --git a/chef/cookbooks/python/src/15/calling_python_from_c/embed.c b/chef/cookbooks/python/src/15/calling_python_from_c/embed.c new file mode 100644 index 0000000..2523a95 --- /dev/null +++ b/chef/cookbooks/python/src/15/calling_python_from_c/embed.c @@ -0,0 +1,84 @@ +#include + +/* Execute func(x,y) in the Python interpreter. The + arguments and return result of the function must + be Python floats */ + +double call_func(PyObject *func, double x, double y) { + PyObject *args; + PyObject *kwargs; + PyObject *result = 0; + double retval; + + /* Make sure we own the GIL */ + PyGILState_STATE state = PyGILState_Ensure(); + + /* Verify that func is a proper callable */ + if (!PyCallable_Check(func)) { + fprintf(stderr,"call_func: expected a callable\n"); + goto fail; + } + /* Build arguments */ + args = Py_BuildValue("(dd)", x, y); + kwargs = NULL; + + /* Call the function */ + result = PyObject_Call(func, args, kwargs); + Py_DECREF(args); + Py_XDECREF(kwargs); + + /* Check for Python exceptions (if any) */ + if (PyErr_Occurred()) { + PyErr_Print(); + goto fail; + } + + /* Verify the result is a float object */ + if (!PyFloat_Check(result)) { + fprintf(stderr,"call_func: callable didn't return a float\n"); + goto fail; + } + + /* Create the return value */ + retval = PyFloat_AsDouble(result); + Py_DECREF(result); + + /* Restore previous GIL state and return */ + PyGILState_Release(state); + return retval; + +fail: + Py_XDECREF(result); + PyGILState_Release(state); + abort(); +} + + +/* Load a symbol from a module */ +PyObject *import_name(const char *modname, const char *symbol) { + PyObject *u_name, *module; + u_name = PyUnicode_FromString(modname); + module = PyImport_Import(u_name); + Py_DECREF(u_name); + return PyObject_GetAttrString(module, symbol); +} + +/* Simple embedding example */ +int main() { + PyObject *pow_func; + double x; + + Py_Initialize(); + /* Get a reference to the math.pow function */ + pow_func = import_name("math","pow"); + + /* Call it using our call_func() code */ + for (x = 0.0; x < 10.0; x += 0.1) { + printf("%0.2f %0.2f\n", x, call_func(pow_func,x,2.0)); + } + /* Done */ + Py_DECREF(pow_func); + Py_Finalize(); + return 0; +} + diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py new file mode 100644 index 0000000..2954533 --- /dev/null +++ b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/example.py @@ -0,0 +1,10 @@ +import sample + +sample.consume_iterable([1,2,3,4]) + +def countdown(n): + while n > 0: + yield n + n -= 1 + +sample.consume_iterable(countdown(10)) diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c new file mode 100644 index 0000000..ebdba94 --- /dev/null +++ b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/sample.c @@ -0,0 +1,43 @@ +#include "Python.h" + +static PyObject *py_consume_iterable(PyObject *self, PyObject *args) { + PyObject *obj; + PyObject *iter; + PyObject *item; + + if (!PyArg_ParseTuple(args, "O", &obj)) { + return NULL; + } + if ((iter = PyObject_GetIter(obj)) == NULL) { + return NULL; + } + while ((item = PyIter_Next(iter)) != NULL) { + /* Use item */ + PyObject_Print(item, stdout, 0); + printf("\n"); + Py_DECREF(item); + } + Py_DECREF(iter); + return Py_BuildValue(""); +} + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"consume_iterable", py_consume_iterable, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/consuming_an_iterable_from_c/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt new file mode 100644 index 0000000..ea993e9 --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/README.txt @@ -0,0 +1,4 @@ +To build, you need to perform these two steps: + +% python3 setup.py build_ext --inplace +% python3 ptsetup.py build_ext --inplace diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py new file mode 100644 index 0000000..150772b --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/example.py @@ -0,0 +1,4 @@ +import sample +import ptexample +p1 = sample.Point(2,3) +ptexample.print_point(p1) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c new file mode 100644 index 0000000..c8b3bb0 --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptexample.c @@ -0,0 +1,50 @@ +/* ptexample.c */ + +/* Include the header associated with the other module */ +#include "pysample.h" + +/* An extension function that uses the exported API */ +static PyObject *print_point(PyObject *self, PyObject *args) { + PyObject *obj; + Point *p; + if (!PyArg_ParseTuple(args,"O", &obj)) { + return NULL; + } + + /* Note: This is defined in a different module */ + p = PyPoint_AsPoint(obj); + if (!p) { + return NULL; + } + printf("%f %f\n", p->x, p->y); + return Py_BuildValue(""); +} + +static PyMethodDef PtExampleMethods[] = { + {"print_point", print_point, METH_VARARGS, "output a point"}, + { NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef ptexamplemodule = { + PyModuleDef_HEAD_INIT, + "ptexample", /* name of module */ + "A module that imports an API", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + PtExampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_ptexample(void) { + PyObject *m; + + m = PyModule_Create(&ptexamplemodule); + if (m == NULL) + return NULL; + + /* Import sample, loading its API functions */ + if (!import_sample()) { + return NULL; + } + return m; +} diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py new file mode 100644 index 0000000..5339d09 --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/ptsetup.py @@ -0,0 +1,11 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="ptexample", + ext_modules=[ + Extension("ptexample", + ["ptexample.c"], + include_dirs = ['..','.'], # May need pysample.h directory + ) + ] +) diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c new file mode 100644 index 0000000..0bf1cef --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.c @@ -0,0 +1,87 @@ +#include "Python.h" +#define PYSAMPLE_MODULE +#include "pysample.h" + +/* Destructor function for points */ +static void del_Point(PyObject *obj) { + free(PyCapsule_GetPointer(obj,"Point")); +} + +/* Utility functions */ +static Point *PyPoint_AsPoint(PyObject *obj) { + return (Point *) PyCapsule_GetPointer(obj, "Point"); +} + +static PyObject *PyPoint_FromPoint(Point *p, int must_free) { + return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); +} + +/* Create a new Point object */ +static PyObject *py_Point(PyObject *self, PyObject *args) { + Point *p; + double x,y; + if (!PyArg_ParseTuple(args,"dd",&x,&y)) { + return NULL; + } + p = (Point *) malloc(sizeof(Point)); + p->x = x; + p->y = y; + return PyPoint_FromPoint(p, 1); +} + +static PyObject *py_distance(PyObject *self, PyObject *args) { + Point *p1, *p2; + PyObject *py_p1, *py_p2; + double result; + + if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { + return NULL; + } + if (!(p1 = PyPoint_AsPoint(py_p1))) { + return NULL; + } + if (!(p2 = PyPoint_AsPoint(py_p2))) { + return NULL; + } + result = distance(p1,p2); + return Py_BuildValue("d", result); +} + +static _PointAPIMethods _point_api = { + PyPoint_AsPoint, + PyPoint_FromPoint +}; + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"Point", py_Point, METH_VARARGS, "Make a point"}, + {"distance", py_distance, METH_VARARGS, "Distance between points"}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + PyObject *m; + PyObject *py_point_api; + + m = PyModule_Create(&samplemodule); + if (m == NULL) + return NULL; + + /* Add the Point C API functions */ + py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL); + if (py_point_api) { + PyModule_AddObject(m, "_point_api", py_point_api); + } + return m; +} diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h new file mode 100644 index 0000000..6d00e46 --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/pysample.h @@ -0,0 +1,31 @@ +/* pysample.h */ +#include "Python.h" +#include "sample.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* Public API Table */ +typedef struct { + Point *(*aspoint)(PyObject *); + PyObject *(*frompoint)(Point *, int); +} _PointAPIMethods; + +#ifndef PYSAMPLE_MODULE +/* Method table in external module */ +static _PointAPIMethods *_point_api = 0; + +/* Import the API table from sample */ +static int import_sample(void) { + _point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0); + return (_point_api != NULL) ? 1 : 0; +} + +/* Macros to implement the programming interface */ +#define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj) +#define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py new file mode 100644 index 0000000..5baf554 --- /dev/null +++ b/chef/cookbooks/python/src/15/defining_and_exporting_c_apis_from_extension_modules/setup.py @@ -0,0 +1,11 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["../sample.c", "pysample.c"], + include_dirs = ['..'], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py new file mode 100644 index 0000000..6a8e3a6 --- /dev/null +++ b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/example.py @@ -0,0 +1,20 @@ +# example.py +import sample + +def foo(): + print('About to die') + sample.die() + +def bar(): + print('About to call the function that dies') + foo() + +def spam(): + print('About to call the function that calls the function that dies') + bar() + +if __name__ == '__main__': + import faulthandler + faulthandler.enable() + spam() + diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c new file mode 100644 index 0000000..24bf6c5 --- /dev/null +++ b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/sample.c @@ -0,0 +1,31 @@ +#include +#include + +static PyObject *py_die(PyObject *self, PyObject *args) { + char *s = 0; + + *s = 'x'; + Py_RETURN_NONE; +} + + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"die", py_die, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/diagnosing_segmentation_faults/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py new file mode 100644 index 0000000..d6dcad7 --- /dev/null +++ b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/example.py @@ -0,0 +1,9 @@ +import sample +p1 = sample.Point(2, 3) +p2 = sample.Point(4, 5) +print(p1) +print(p2) +print(sample.distance(p1, p2)) +del p1 +del p2 +print('Done') diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c new file mode 100644 index 0000000..4fad317 --- /dev/null +++ b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/pysample.c @@ -0,0 +1,70 @@ +#include "Python.h" +#include "sample.h" + +/* Destructor function for points */ +static void del_Point(PyObject *obj) { + free(PyCapsule_GetPointer(obj,"Point")); +} + +/* Utility functions */ +static Point *PyPoint_AsPoint(PyObject *obj) { + return (Point *) PyCapsule_GetPointer(obj, "Point"); +} + +static PyObject *PyPoint_FromPoint(Point *p, int must_free) { + return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); +} + +/* Create a new Point object */ +static PyObject *py_Point(PyObject *self, PyObject *args) { + Point *p; + double x,y; + if (!PyArg_ParseTuple(args,"dd",&x,&y)) { + return NULL; + } + p = (Point *) malloc(sizeof(Point)); + p->x = x; + p->y = y; + return PyPoint_FromPoint(p, 1); +} + +static PyObject *py_distance(PyObject *self, PyObject *args) { + Point *p1, *p2; + PyObject *py_p1, *py_p2; + double result; + + if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { + return NULL; + } + if (!(p1 = PyPoint_AsPoint(py_p1))) { + return NULL; + } + if (!(p2 = PyPoint_AsPoint(py_p2))) { + return NULL; + } + result = distance(p1,p2); + return Py_BuildValue("d", result); +} + + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"Point", py_Point, METH_VARARGS, "Make a point"}, + {"distance", py_distance, METH_VARARGS, "Distance between points"}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py new file mode 100644 index 0000000..5baf554 --- /dev/null +++ b/chef/cookbooks/python/src/15/managing_opaque_pointers_in_c_extension_modules/setup.py @@ -0,0 +1,11 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["../sample.c", "pysample.c"], + include_dirs = ['..'], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py new file mode 100644 index 0000000..106c785 --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/example.py @@ -0,0 +1,15 @@ +import sample +import sys + +sample.print_chars(b'hello world') + +s = 'Spicy Jalape\u00f1o' +print(sys.getsizeof(s)) +sample.print_chars_str(s) +print(sys.getsizeof(s)) +del s + +s = 'spicy Jalape\u00f1o' +print(sys.getsizeof(s)) +sample.print_chars_str_alt(s) +print(sys.getsizeof(s)) diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c new file mode 100644 index 0000000..e8e9caa --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/sample.c @@ -0,0 +1,68 @@ +#include +#include + +void print_chars(char *s) { + while (*s) { + printf("%2x ", (unsigned char) *s); + s++; + } + printf("\n"); +} + +static PyObject *py_print_chars(PyObject *self, PyObject *args) { + char *s; + + if (!PyArg_ParseTuple(args, "y", &s)) { + return NULL; + } + print_chars(s); + Py_RETURN_NONE; +} + +static PyObject *py_print_chars_str(PyObject *self, PyObject *args) { + char *s; + + if (!PyArg_ParseTuple(args, "s", &s)) { + return NULL; + } + print_chars(s); + Py_RETURN_NONE; +} + +static PyObject *py_print_chars_str_alt(PyObject *self, PyObject *args) { + PyObject *o, *bytes; + char *s; + + if (!PyArg_ParseTuple(args, "U", &o)) { + return NULL; + } + bytes = PyUnicode_AsUTF8String(o); + s = PyBytes_AsString(bytes); + print_chars(s); + Py_DECREF(bytes); + Py_RETURN_NONE; +} + + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"print_chars", py_print_chars, METH_VARARGS}, + {"print_chars_str", py_print_chars_str, METH_VARARGS}, + {"print_chars_str_alt", py_print_chars_str_alt, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_null_terminated_strings_to_c_libraries/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py new file mode 100644 index 0000000..6253f7e --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/example.py @@ -0,0 +1,4 @@ +import sample +s = "Spicy Jalape\u00f1o" +sample.print_chars(s) +sample.print_wchars(s) diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c new file mode 100644 index 0000000..43ea23b --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/sample.c @@ -0,0 +1,65 @@ +#include +#include + +void print_chars(char *s, int len) { + int n = 0; + while (n < len) { + printf("%2x ", (unsigned char) s[n]); + n++; + } + printf("\n"); +} + +void print_wchars(wchar_t *s, int len) { + int n = 0; + while (n < len) { + printf("%x ", s[n]); + n++; + } + printf("\n"); +} + +static PyObject *py_print_chars(PyObject *self, PyObject *args) { + char *s; + Py_ssize_t len; + + if (!PyArg_ParseTuple(args, "s#", &s, &len)) { + return NULL; + } + print_chars(s, len); + Py_RETURN_NONE; +} + +static PyObject *py_print_wchars(PyObject *self, PyObject *args) { + wchar_t *s; + Py_ssize_t len; + + if (!PyArg_ParseTuple(args, "u#", &s, &len)) { + return NULL; + } + print_wchars(s,len); + Py_RETURN_NONE; +} + + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"print_chars", py_print_chars, METH_VARARGS}, + {"print_wchars", py_print_wchars, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/passing_unicode_strings_to_c_libraries/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py new file mode 100644 index 0000000..318c4cc --- /dev/null +++ b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/example.py @@ -0,0 +1,6 @@ +f = open('sample.c') +import sample +sample.consume_file(f) +f.close() + +print('**** DONE') diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c new file mode 100644 index 0000000..ae2b8e4 --- /dev/null +++ b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/sample.c @@ -0,0 +1,84 @@ +#include "Python.h" + +#define CHUNK_SIZE 8192 + +/* Consume a "file-like" object and write bytes to stdout */ +static PyObject *py_consume_file(PyObject *self, PyObject *args) { + PyObject *obj; + PyObject *read_meth; + PyObject *result = NULL; + PyObject *read_args; + + if (!PyArg_ParseTuple(args,"O", &obj)) { + return NULL; + } + + /* Get the read method of the passed object */ + if ((read_meth = PyObject_GetAttrString(obj, "read")) == NULL) { + return NULL; + } + + /* Build the argument list to read() */ + read_args = Py_BuildValue("(i)", CHUNK_SIZE); + while (1) { + PyObject *data; + PyObject *enc_data; + char *buf; + Py_ssize_t len; + + /* Call read() */ + if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) { + goto final; + } + + /* Check for EOF */ + if (PySequence_Length(data) == 0) { + Py_DECREF(data); + break; + } + + /* Encode Unicode as Bytes for C */ + if ((enc_data = PyUnicode_AsEncodedString(data, "utf-8", "strict")) == NULL) { + Py_DECREF(data); + goto final; + } + + /* Extract underlying buffer data */ + PyBytes_AsStringAndSize(enc_data, &buf, &len); + + /* Write to stdout (replace with something more useful) */ + write(1, buf, len); + + /* Cleanup */ + Py_DECREF(enc_data); + Py_DECREF(data); + } + result = Py_BuildValue(""); + + final: + /* Cleanup */ + Py_DECREF(read_meth); + Py_DECREF(read_args); + return result; +} + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"consume_file", py_consume_file, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/reading_file_like_objects_from_c/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/sample.c b/chef/cookbooks/python/src/15/sample.c new file mode 100644 index 0000000..aff2306 --- /dev/null +++ b/chef/cookbooks/python/src/15/sample.c @@ -0,0 +1,53 @@ +/* sample.c */ +#include + +/* Compute the greatest common divisor */ +int gcd(int x, int y) { + int g = y; + while (x > 0) { + g = x; + x = y % x; + y = g; + } + return g; +} + +/* Test if (x0,y0) is in the Mandelbrot set or not */ +int in_mandel(double x0, double y0, int n) { + double x=0,y=0,xtemp; + while (n > 0) { + xtemp = x*x - y*y + x0; + y = 2*x*y + y0; + x = xtemp; + n -= 1; + if (x*x + y*y > 4) return 0; + } + return 1; +} + +/* Divide two numbers */ +int divide(int a, int b, int *remainder) { + int quot = a / b; + *remainder = a % b; + return quot; +} + +/* Average values in an array */ +double avg(double *a, int n) { + int i; + double total = 0.0; + for (i = 0; i < n; i++) { + total += a[i]; + } + return total / n; +} + +/* A C data structure */ +typedef struct Point { + double x,y; +} Point; + +/* Function involving a C data structure */ +double distance(Point *p1, Point *p2) { + return hypot(p1->x - p2->x, p1->y - p2->y); +} diff --git a/chef/cookbooks/python/src/15/sample.h b/chef/cookbooks/python/src/15/sample.h new file mode 100644 index 0000000..dd42a62 --- /dev/null +++ b/chef/cookbooks/python/src/15/sample.h @@ -0,0 +1,12 @@ +/* sample.h */ + +extern int gcd(int x, int y); +extern int in_mandel(double x0, double y0, int n); +extern int divide(int a, int b, int *remainder); +extern double avg(double *a, int n); + +typedef struct Point { + double x,y; +} Point; + +extern double distance(Point *p1, Point *p2); diff --git a/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py b/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py new file mode 100644 index 0000000..be2aa77 --- /dev/null +++ b/chef/cookbooks/python/src/15/turning_a_function_pointer_into_a_callable/example.py @@ -0,0 +1,16 @@ +import ctypes +lib = ctypes.cdll.LoadLibrary(None) + +# Get the address of sin() from the C math library +addr = ctypes.cast(lib.sin, ctypes.c_void_p).value +print(addr) +140735505915760 + +# Turn the address into a callable function +functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double) +func = functype(addr) +print(func) + +# Call the resulting function +print(func(2)) +print(func(0)) diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py new file mode 100644 index 0000000..a231e46 --- /dev/null +++ b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/example.py @@ -0,0 +1,34 @@ +# array module example +import sample +import array +a = array.array('d',[1,-3,4,7,2,0]) +print(a) +sample.clip(a,1,4,a) +print(a) + +# numpy example +import numpy +b = numpy.random.uniform(-10,10,size=1000000) +print(b) +c = numpy.zeros_like(b) +print(c) +sample.clip(b,-5,5,c) +print(c) +print(min(c)) +print(max(c)) + +# Timing test +from timeit import timeit +print('numpy.clip') +print(timeit('numpy.clip(b,-5,5,c)', 'from __main__ import b,c,numpy', number=1000)) +print('sample.clip') +print(timeit('sample.clip(b,-5,5,c)', 'from __main__ import b,c,sample', number=1000)) + +print('sample.clip_fast') +print(timeit('sample.clip_fast(b,-5,5,c)', 'from __main__ import b,c,sample', number=1000)) + +# 2D test +d = numpy.random.uniform(-10,10,size=(1000,1000)) +print(d) +sample.clip2d(d, -5, 5, d) +print(d) diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx new file mode 100644 index 0000000..2bbbaaa --- /dev/null +++ b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/sample.pyx @@ -0,0 +1,46 @@ +cimport cython + +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef clip(double[:] a, double min, double max, double[:] out): + ''' + Clip the values in a to be between min and max. Result in out + ''' + if min > max: + raise ValueError("min must be <= max") + if a.shape[0] != out.shape[0]: + raise ValueError("input and output arrays must be the same size") + for i in range(a.shape[0]): + if a[i] < min: + out[i] = min + elif a[i] > max: + out[i] = max + else: + out[i] = a[i] + +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef clip_fast(double[:] a, double min, double max, double[:] out): + if min > max: + raise ValueError("min must be <= max") + if a.shape[0] != out.shape[0]: + raise ValueError("input and output arrays must be the same size") + for i in range(a.shape[0]): + out[i] = (a[i] if a[i] < max else max) if a[i] > min else min + +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef clip2d(double[:,:] a, double min, double max, double[:,:] out): + if min > max: + raise ValueError("min must be <= max") + for n in range(a.ndim): + if a.shape[n] != out.shape[n]: + raise TypeError("a and out have different shapes") + for i in range(a.shape[0]): + for j in range(a.shape[1]): + if a[i,j] < min: + out[i,j] = min + elif a[i,j] > max: + out[i,j] = max + else: + out[i,j] = a[i,j] diff --git a/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py new file mode 100644 index 0000000..01e1bae --- /dev/null +++ b/chef/cookbooks/python/src/15/using_cython_to_write_high_performance_array_operations/setup.py @@ -0,0 +1,15 @@ + +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +ext_modules = [ + Extension("sample", + ["sample.pyx"]) +] + +setup( + name = 'Sample app', + cmdclass = {'build_ext': build_ext}, + ext_modules = ext_modules +) diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py new file mode 100644 index 0000000..f021321 --- /dev/null +++ b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/example.py @@ -0,0 +1,4 @@ +import sample +s = sample.retstr() +print(repr(s)) +sample.print_chars(s) diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c new file mode 100644 index 0000000..ed0181b --- /dev/null +++ b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/sample.c @@ -0,0 +1,64 @@ +#include +#include +/* Some dubious string data (malformed UTF-8) */ +const char *sdata = "Spicy Jalape\xc3\xb1o\xae"; +int slen = 16; + +/* Output character data */ +void print_chars(char *s, int len) { + int n = 0; + while (n < len) { + printf("%2x ", (unsigned char) s[n]); + n++; + } + printf("\n"); +} + +/* Return the C string back to Python */ +static PyObject *py_retstr(PyObject *self, PyObject *args) { + if (!PyArg_ParseTuple(args, "")) { + return NULL; + } + return PyUnicode_Decode(sdata, slen, "utf-8", "surrogateescape"); +} + +/* Wrapper for the print_chars() function */ +static PyObject *py_print_chars(PyObject *self, PyObject *args) { + PyObject *obj, *bytes; + char *s = 0; + Py_ssize_t len; + + if (!PyArg_ParseTuple(args, "U", &obj)) { + return NULL; + } + + if ((bytes = PyUnicode_AsEncodedString(obj,"utf-8","surrogateescape")) == NULL) { + return NULL; + } + PyBytes_AsStringAndSize(bytes, &s, &len); + print_chars(s, len); + Py_DECREF(bytes); + Py_RETURN_NONE; +} + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"print_chars", py_print_chars, METH_VARARGS}, + {"retstr", py_retstr, METH_VARARGS}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py new file mode 100644 index 0000000..1113893 --- /dev/null +++ b/chef/cookbooks/python/src/15/working_with_c_strings_of_dubious_encoding/setup.py @@ -0,0 +1,10 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["sample.c"], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py new file mode 100644 index 0000000..64b6190 --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/example.py @@ -0,0 +1,12 @@ +import sample +print(sample.gcd(42, 8)) +print(sample.divide(42, 8)) +p1 = sample.Point(2, 3) +p2 = sample.Point(4, 5) +print(sample.distance(p1, p2)) +print(p1.x) +print(p1.y) + +import array +a = array.array('d', [1, 2, 3]) +print(sample.avg(a)) diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i new file mode 100644 index 0000000..0407dac --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/sample.i @@ -0,0 +1,53 @@ +// sample.i - Swig interface +%module sample +%{ +#include "sample.h" +%} + +/* Customizations */ +%extend Point { + /* Constructor for Point objects */ + Point(double x, double y) { + Point *p = (Point *) malloc(sizeof(Point)); + p->x = x; + p->y = y; + return p; + }; +}; + +/* Map int *remainder as an output argument */ +%include typemaps.i +%apply int *OUTPUT { int * remainder }; + +/* Map the argument pattern (double *a, int n) to arrays */ +%typemap(in) (double *a, int n)(Py_buffer view) { + view.obj = NULL; + if (PyObject_GetBuffer($input, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { + SWIG_fail; + } + if (strcmp(view.format,"d") != 0) { + PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); + SWIG_fail; + } + $1 = (double *) view.buf; + $2 = view.len / sizeof(double); +} + +%typemap(freearg) (double *a, int n) { + if (view$argnum.obj) { + PyBuffer_Release(&view$argnum); + } +} + +/* C declarations to be included in the extension module */ + +extern int gcd(int, int); +extern int in_mandel(double x0, double y0, int n); +extern int divide(int a, int b, int *remainder); +extern double avg(double *a, int n); + +typedef struct Point { + double x,y; +} Point; + +extern double distance(Point *p1, Point *p2); diff --git a/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py new file mode 100644 index 0000000..49b5c5e --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_c_code_with_swig/setup.py @@ -0,0 +1,16 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name='sample', + py_modules=['sample.py'], + ext_modules=[ + Extension('_sample', + ['../sample.c', 'sample_wrap.c'], + include_dirs = ['..'], + define_macros = [], + undef_macros = [], + library_dirs = [], + libraries = [] + ) + ] +) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd new file mode 100644 index 0000000..981c461 --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/csample.pxd @@ -0,0 +1,15 @@ +# csample.pxd +# +# Declarations of "external" C functions and structures + +cdef extern from "sample.h": + int gcd(int, int) + bint in_mandel(double, double, int) + int divide(int, int, int *) + double avg(double *, int) nogil + + ctypedef struct Point: + double x + double y + + double distance(Point *, Point *) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py new file mode 100644 index 0000000..6db2ea6 --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/example.py @@ -0,0 +1,12 @@ +import sample +print(sample.gcd(42, 8)) +print(sample.divide(42, 8)) +p1 = sample.Point(2, 3) +p2 = sample.Point(4, 5) +print(p1) +print(p2) +print(sample.distance(p1, p2)) + +import array +a = array.array('d', [1, 2, 3]) +print(sample.avg(a)) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx new file mode 100644 index 0000000..c99a665 --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample.pyx @@ -0,0 +1,50 @@ +# sample.pyx + +# Import the low-level C declarations +cimport csample + +# Import some functionality from Python and the C stdlib +from cpython.pycapsule cimport * +from libc.stdlib cimport malloc, free + +# Wrappers +def gcd(unsigned int x, unsigned int y): + return csample.gcd(x,y) + +def in_mandel(x,y,unsigned int n): + return csample.in_mandel(x,y,n) + +def divide(x,y): + cdef int rem + quot = csample.divide(x,y,&rem) + return quot, rem + +def avg(double[:] a): + cdef: + int sz + double result + + sz = a.size + with nogil: + result = csample.avg( &a[0], sz) + return result + +# Destructor for cleaning up Point objects +cdef del_Point(object obj): + pt = PyCapsule_GetPointer(obj,"Point") + free( pt) + +# Create a Point object and return as a capsule +def Point(double x,double y): + cdef csample.Point *p + p = malloc(sizeof(csample.Point)) + if p == NULL: + raise MemoryError("No memory to make a Point") + p.x = x + p.y = y + return PyCapsule_New(p,"Point",del_Point) + +def distance(p1, p2): + pt1 = PyCapsule_GetPointer(p1,"Point") + pt2 = PyCapsule_GetPointer(p2,"Point") + return csample.distance(pt1,pt2) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx new file mode 100644 index 0000000..b4f394c --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/sample_alt.pyx @@ -0,0 +1,54 @@ +# sample_alt.pyx + +# Import the low-level C declarations +cimport csample + +# Import some functionality from Python and the C stdlib +from libc.stdlib cimport malloc, free + +# Wrappers +def gcd(unsigned int x, unsigned int y): + return csample.gcd(x,y) + +def in_mandel(x,y,unsigned int n): + return csample.in_mandel(x,y,n) + +def divide(x,y): + cdef int rem + quot = csample.divide(x,y,&rem) + return quot, rem + +def avg(double[:] a): + cdef: + int sz + double result + + sz = a.size + with nogil: + result = csample.avg( &a[0], sz) + return result + +cdef class Point: + cdef csample.Point *_c_point + def __cinit__(self, double x, double y): + self._c_point = malloc(sizeof(csample.Point)) + self._c_point.x = x + self._c_point.y = y + + def __dealloc__(self): + free(self._c_point) + + property x: + def __get__(self): + return self._c_point.x + def __set__(self, value): + self._c_point.x = value + + property y: + def __get__(self): + return self._c_point.y + def __set__(self, value): + self._c_point.y = value + +def distance(Point p1, Point p2): + return csample.distance(p1._c_point, p2._c_point) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py new file mode 100644 index 0000000..9c3369e --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup.py @@ -0,0 +1,15 @@ +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +ext_modules = [ + Extension("sample", + ["sample.pyx"], + include_dirs=['..'], + libraries=['sample'], + library_dirs=['..'])] +setup( + name = 'Sample extension module', + cmdclass = {'build_ext': build_ext}, + ext_modules = ext_modules +) diff --git a/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py new file mode 100644 index 0000000..7f22eb2 --- /dev/null +++ b/chef/cookbooks/python/src/15/wrapping_existing_c_code_with_cython/setup_alt.py @@ -0,0 +1,15 @@ +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +ext_modules = [ + Extension("sample", + ["sample_alt.pyx"], + include_dirs=['..'], + libraries=['sample'], + library_dirs=['..'])] +setup( + name = 'Sample extension module', + cmdclass = {'build_ext': build_ext}, + ext_modules = ext_modules +) diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py new file mode 100644 index 0000000..da1f5b0 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/example.py @@ -0,0 +1,5 @@ +import sample +print(sample.gcd(35,42)) +print(sample.in_mandel(0,0,500)) +print(sample.in_mandel(2.0,1.0,500)) +print(sample.divide(42,8)) diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c new file mode 100644 index 0000000..02b01b6 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/pysample.c @@ -0,0 +1,59 @@ +#include "Python.h" +#include "sample.h" + +/* int gcd(int, int) */ +static PyObject *py_gcd(PyObject *self, PyObject *args) { + int x, y, result; + + if (!PyArg_ParseTuple(args,"ii", &x, &y)) { + return NULL; + } + result = gcd(x,y); + return Py_BuildValue("i", result); +} + +/* int in_mandel(double, double, int) */ +static PyObject *py_in_mandel(PyObject *self, PyObject *args) { + double x0, y0; + int n; + int result; + + if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) { + return NULL; + } + result = in_mandel(x0,y0,n); + return Py_BuildValue("i", result); +} + +/* int divide(int, int, int *) */ +static PyObject *py_divide(PyObject *self, PyObject *args) { + int a, b, quotient, remainder; + if (!PyArg_ParseTuple(args, "ii", &a, &b)) { + return NULL; + } + quotient = divide(a,b, &remainder); + return Py_BuildValue("(ii)", quotient, remainder); +} + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"}, + {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"}, + {"divide", py_divide, METH_VARARGS, "Integer division"}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py new file mode 100644 index 0000000..5baf554 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_a_simple_c_extension_module/setup.py @@ -0,0 +1,11 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["../sample.c", "pysample.c"], + include_dirs = ['..'], + ) + ] +) diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py new file mode 100644 index 0000000..9f9ddc5 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/example.py @@ -0,0 +1,10 @@ +import array +from sample import avg + +print(avg(array.array('d',[1,2,3]))) +try: + import numpy + print(avg(numpy.array([1., 2., 3.]))) +except ImportError: + pass + diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c new file mode 100644 index 0000000..5f29b62 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/pysample.c @@ -0,0 +1,59 @@ +#include "Python.h" +#include "sample.h" + +/* Call double avg(double *, int) */ +static PyObject *py_avg(PyObject *self, PyObject *args) { + PyObject *bufobj; + Py_buffer view; + double result; + /* Get the passed Python object */ + if (!PyArg_ParseTuple(args, "O", &bufobj)) { + return NULL; + } + + /* Attempt to extract buffer information from it */ + if (PyObject_GetBuffer(bufobj, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { + return NULL; + } + + if (view.ndim != 1) { + PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array"); + PyBuffer_Release(&view); + return NULL; + } + + /* Check the type of items in the array */ + if (strcmp(view.format,"d") != 0) { + PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); + PyBuffer_Release(&view); + return NULL; + } + + /* Pass the raw buffer and size to the C function */ + result = avg(view.buf, view.shape[0]); + + /* Indicate we're done working with the buffer */ + PyBuffer_Release(&view); + return Py_BuildValue("d", result); +} + +/* Module method table */ +static PyMethodDef SampleMethods[] = { + {"avg", py_avg, METH_VARARGS, "Average"}, + { NULL, NULL, 0, NULL} +}; + +/* Module structure */ +static struct PyModuleDef samplemodule = { + PyModuleDef_HEAD_INIT, + "sample", /* name of module */ + "A sample module", /* Doc string (may be NULL) */ + -1, /* Size of per-interpreter state or -1 */ + SampleMethods /* Method table */ +}; + +/* Module initialization function */ +PyMODINIT_FUNC +PyInit_sample(void) { + return PyModule_Create(&samplemodule); +} diff --git a/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py new file mode 100644 index 0000000..5baf554 --- /dev/null +++ b/chef/cookbooks/python/src/15/writing_an_extension_function_that_operates_on_arrays/setup.py @@ -0,0 +1,11 @@ +# setup.py +from distutils.core import setup, Extension + +setup(name="sample", + ext_modules=[ + Extension("sample", + ["../sample.c", "pysample.c"], + include_dirs = ['..'], + ) + ] +) diff --git a/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py b/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py new file mode 100644 index 0000000..dfd82e3 --- /dev/null +++ b/chef/cookbooks/python/src/2/combining_and_concatenating_strings/example.py @@ -0,0 +1,38 @@ +# example.py +# +# Example of combining text via generators + +def sample(): + yield "Is" + yield "Chicago" + yield "Not" + yield "Chicago?" + +# (a) Simple join operator +text = ''.join(sample()) +print(text) + +# (b) Redirection of parts to I/O +import sys +for part in sample(): + sys.stdout.write(part) +sys.stdout.write('\n') + +# (c) Combination of parts into buffers and larger I/O operations +def combine(source, maxsize): + parts = [] + size = 0 + for part in source: + parts.append(part) + size += len(part) + if size > maxsize: + yield ''.join(parts) + parts = [] + size = 0 + yield ''.join(parts) + +for part in combine(sample(), 32768): + sys.stdout.write(part) +sys.stdout.write('\n') + + diff --git a/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py b/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py new file mode 100644 index 0000000..d351b84 --- /dev/null +++ b/chef/cookbooks/python/src/2/matching_and_searching_for_text_patterns_using_regular_expressions/example.py @@ -0,0 +1,23 @@ +# example.py +# +# Examples of simple regular expression matching + +import re + +# Some sample text +text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' + +# (a) Find all matching dates +datepat = re.compile(r'\d+/\d+/\d+') +print(datepat.findall(text)) + +# (b) Find all matching dates with capture groups +datepat = re.compile(r'(\d+)/(\d+)/(\d+)') +for month, day, year in datepat.findall(text): + print('{}-{}-{}'.format(year, month, day)) + +# (c) Iterative search +for m in datepat.finditer(text): + print(m.groups()) + + diff --git a/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py b/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py new file mode 100644 index 0000000..6e5d0e7 --- /dev/null +++ b/chef/cookbooks/python/src/2/matching_strings_using_shell_wildcard_patterns/example.py @@ -0,0 +1,19 @@ +# example.py +# +# Example of using shell-wildcard style matching in list comprehensions + +from fnmatch import fnmatchcase as match + +addresses = [ + '5412 N CLARK ST', + '1060 W ADDISON ST', + '1039 W GRANVILLE AVE', + '2122 N CLARK ST', + '4802 N BROADWAY', +] + +a = [addr for addr in addresses if match(addr, '* ST')] +print(a) + +b = [addr for addr in addresses if match(addr, '54[0-9][0-9] *CLARK*')] +print(b) diff --git a/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py b/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py new file mode 100644 index 0000000..49a61e9 --- /dev/null +++ b/chef/cookbooks/python/src/2/normalizing_unicode_text_to_a_standard_representation/example.py @@ -0,0 +1,28 @@ +# example.py +# +# Example of unicode normalization + +# Two strings +s1 = 'Spicy Jalape\u00f1o' +s2 = 'Spicy Jalapen\u0303o' + +# (a) Print them out (usually looks identical) +print(s1) +print(s2) + +# (b) Examine equality and length +print('s1 == s2', s1 == s2) +print(len(s1), len(s2)) + +# (c) Normalize and try the same experiment +import unicodedata + +n_s1 = unicodedata.normalize('NFC', s1) +n_s2 = unicodedata.normalize('NFC', s2) + +print('n_s1 == n_s2', n_s1 == n_s2) +print(len(n_s1), len(n_s2)) + +# (d) Example of normalizing to a decomposed form and stripping accents +t1 = unicodedata.normalize('NFD', s1) +print(''.join(c for c in t1 if not unicodedata.combining(c))) diff --git a/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py b/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py new file mode 100644 index 0000000..3cfcea8 --- /dev/null +++ b/chef/cookbooks/python/src/2/reformatting_text_to_fixed_number_of_columns/example.py @@ -0,0 +1,23 @@ +# example.py +# +# Examples of reformatting text to different column widths + +# A long string +s = "Look into my eyes, look into my eyes, the eyes, the eyes, \ +the eyes, not around the eyes, don't look around the eyes, \ +look into my eyes, you're under." + +import textwrap + +print(textwrap.fill(s, 70)) +print() + +print(textwrap.fill(s, 40)) +print() + +print(textwrap.fill(s, 40, initial_indent=' ')) +print() + +print(textwrap.fill(s, 40, subsequent_indent=' ')) +print() + diff --git a/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py b/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py new file mode 100644 index 0000000..905e5aa --- /dev/null +++ b/chef/cookbooks/python/src/2/sanitizing_and_cleaning_up_text/example.py @@ -0,0 +1,31 @@ +# example.py +# +# Example of some tricky sanitization problems + +# A tricky string +s = 'p\xfdt\u0125\xf6\xf1\x0cis\tawesome\r\n' +print(s) + +# (a) Remapping whitespace +remap = { + ord('\t') : ' ', + ord('\f') : ' ', + ord('\r') : None # Deleted +} + +a = s.translate(remap) +print('whitespace remapped:', a) + +# (b) Remove all combining characters/marks +import unicodedata +import sys +cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) + if unicodedata.combining(chr(c))) + +b = unicodedata.normalize('NFD', a) +c = b.translate(cmb_chrs) +print('accents removed:', c) + +# (c) Accent removal using I/O decoding +d = b.encode('ascii','ignore').decode('ascii') +print('accents removed via I/O:', d) diff --git a/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py b/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py new file mode 100644 index 0000000..304c575 --- /dev/null +++ b/chef/cookbooks/python/src/2/searching_and_replacing_text/example.py @@ -0,0 +1,22 @@ +# example.py +# +# Examples of simple regular expression substitution + +import re + +# Some sample text +text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' + +datepat = re.compile(r'(\d+)/(\d+)/(\d+)') + +# (a) Simple substitution +print(datepat.sub(r'\3-\1-\2', text)) + +# (b) Replacement function +from calendar import month_abbr + +def change_date(m): + mon_name = month_abbr[int(m.group(1))] + return '{} {} {}'.format(m.group(2), mon_name, m.group(3)) + +print(datepat.sub(change_date, text)) diff --git a/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py b/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py new file mode 100644 index 0000000..4077c6b --- /dev/null +++ b/chef/cookbooks/python/src/2/specifying_a_regular_expression_for_the_shortest_match/example.py @@ -0,0 +1,19 @@ +# example.py +# +# Example of a regular expression that finds shortest matches + +import re + +# Sample text +text = 'Computer says "no." Phone says "yes."' + +# (a) Regex that finds quoted strings - longest match +str_pat = re.compile(r'\"(.*)\"') +print(str_pat.findall(text)) + +# (b) Regex that finds quoted strings - shortest match +str_pat = re.compile(r'\"(.*?)\"') +print(str_pat.findall(text)) + + + diff --git a/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py b/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py new file mode 100644 index 0000000..d752aee --- /dev/null +++ b/chef/cookbooks/python/src/2/splitting_strings_on_any_of_multiple_delimiters/example.py @@ -0,0 +1,29 @@ +# example.py +# +# Example of splitting a string on multiple delimiters using a regex + +import re + +line = 'asdf fjdk; afed, fjek,asdf, foo' + +# (a) Splitting on space, comma, and semicolon +parts = re.split(r'[;,\s]\s*', line) +print(parts) + +# (b) Splitting with a capture group +fields = re.split(r'(;|,|\s)\s*', line) +print(fields) + +# (c) Rebuilding a string using fields above +values = fields[::2] +delimiters = fields[1::2] +delimiters.append('') +print('value =', values) +print('delimiters =', delimiters) +newline = ''.join(v+d for v,d in zip(values, delimiters)) +print('newline =', newline) + +# (d) Splitting using a non-capture group +parts = re.split(r'(?:,|;|\s)\s*', line) +print(parts) + diff --git a/chef/cookbooks/python/src/2/tokenizing_text/example.py b/chef/cookbooks/python/src/2/tokenizing_text/example.py new file mode 100644 index 0000000..db31008 --- /dev/null +++ b/chef/cookbooks/python/src/2/tokenizing_text/example.py @@ -0,0 +1,26 @@ +# example.py +# +# Example of a tokenizer + +import re +from collections import namedtuple + +NAME = r'(?P[a-zA-Z_][a-zA-Z_0-9]*)' +NUM = r'(?P\d+)' +PLUS = r'(?P\+)' +TIMES = r'(?P\*)' +EQ = r'(?P=)' +WS = r'(?P\s+)' + +master_pat = re.compile('|'.join([NAME, NUM, PLUS, TIMES, EQ, WS])) + +Token = namedtuple('Token', ['type','value']) + +def generate_tokens(pat, text): + scanner = pat.scanner(text) + for m in iter(scanner.match, None): + yield Token(m.lastgroup, m.group()) + +for tok in generate_tokens(master_pat, 'foo = 42'): + print(tok) + diff --git a/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py b/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py new file mode 100644 index 0000000..47c83e1 --- /dev/null +++ b/chef/cookbooks/python/src/2/variable_interpolation_in_strings/example.py @@ -0,0 +1,30 @@ +# example.py +# +# Examples of variable interpolation + +# Class for performing safe substitutions +class safesub(dict): + def __missing__(self, key): + return '{%s}' % key + +s = '{name} has {n} messages.' + +# (a) Simple substitution +name = 'Guido' +n = 37 + +print(s.format_map(vars())) + +# (b) Safe substitution with missing values +del n +print(s.format_map(safesub(vars()))) + +# (c) Safe substitution + frame hack +n = 37 +import sys +def sub(text): + return text.format_map(safesub(sys._getframe(1).f_locals)) + +print(sub('Hello {name}')) +print(sub('{name} has {n} messages')) +print(sub('Your favorite color is {color}')) diff --git a/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py b/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py new file mode 100644 index 0000000..237fe09 --- /dev/null +++ b/chef/cookbooks/python/src/2/writing_a_regular_expression_for_multiline_patterns/example.py @@ -0,0 +1,12 @@ +# example.py +# +# Regular expression that matches multiline patterns + +import re + +text = '''/* this is a + multiline comment */ +''' + +comment = re.compile(r'/\*((?:.|\n)*?)\*/') +print(comment.findall(text)) diff --git a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py new file mode 100644 index 0000000..d401566 --- /dev/null +++ b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/example.py @@ -0,0 +1,158 @@ +# example.py +# +# An example of writing a simple recursive descent parser + +import re +import collections + +# Token specification +NUM = r'(?P\d+)' +PLUS = r'(?P\+)' +MINUS = r'(?P-)' +TIMES = r'(?P\*)' +DIVIDE = r'(?P/)' +LPAREN = r'(?P\()' +RPAREN = r'(?P\))' +WS = r'(?P\s+)' + +master_pat = re.compile('|'.join([NUM, PLUS, MINUS, TIMES, + DIVIDE, LPAREN, RPAREN, WS])) + +# Tokenizer +Token = collections.namedtuple('Token', ['type','value']) + +def generate_tokens(text): + scanner = master_pat.scanner(text) + for m in iter(scanner.match, None): + tok = Token(m.lastgroup, m.group()) + if tok.type != 'WS': + yield tok + +# Parser +class ExpressionEvaluator: + ''' + Implementation of a recursive descent parser. Each method + implements a single grammar rule. Use the ._accept() method + to test and accept the current lookahead token. Use the ._expect() + method to exactly match and discard the next token on on the input + (or raise a SyntaxError if it doesn't match). + ''' + + def parse(self,text): + self.tokens = generate_tokens(text) + self.tok = None # Last symbol consumed + self.nexttok = None # Next symbol tokenized + self._advance() # Load first lookahead token + return self.expr() + + def _advance(self): + 'Advance one token ahead' + self.tok, self.nexttok = self.nexttok, next(self.tokens, None) + + def _accept(self,toktype): + 'Test and consume the next token if it matches toktype' + if self.nexttok and self.nexttok.type == toktype: + self._advance() + return True + else: + return False + + def _expect(self,toktype): + 'Consume next token if it matches toktype or raise SyntaxError' + if not self._accept(toktype): + raise SyntaxError('Expected ' + toktype) + + # Grammar rules follow + + def expr(self): + "expression ::= term { ('+'|'-') term }*" + + exprval = self.term() + while self._accept('PLUS') or self._accept('MINUS'): + op = self.tok.type + right = self.term() + if op == 'PLUS': + exprval += right + elif op == 'MINUS': + exprval -= right + return exprval + + def term(self): + "term ::= factor { ('*'|'/') factor }*" + + termval = self.factor() + while self._accept('TIMES') or self._accept('DIVIDE'): + op = self.tok.type + right = self.factor() + if op == 'TIMES': + termval *= right + elif op == 'DIVIDE': + termval /= right + return termval + + def factor(self): + "factor ::= NUM | ( expr )" + + if self._accept('NUM'): + return int(self.tok.value) + elif self._accept('LPAREN'): + exprval = self.expr() + self._expect('RPAREN') + return exprval + else: + raise SyntaxError('Expected NUMBER or LPAREN') + +if __name__ == '__main__': + e = ExpressionEvaluator() + print(e.parse('2')) + print(e.parse('2 + 3')) + print(e.parse('2 + 3 * 4')) + print(e.parse('2 + (3 + 4) * 5')) + +# Example of building trees + +class ExpressionTreeBuilder(ExpressionEvaluator): + def expr(self): + "expression ::= term { ('+'|'-') term }" + + exprval = self.term() + while self._accept('PLUS') or self._accept('MINUS'): + op = self.tok.type + right = self.term() + if op == 'PLUS': + exprval = ('+', exprval, right) + elif op == 'MINUS': + exprval = ('-', exprval, right) + return exprval + + def term(self): + "term ::= factor { ('*'|'/') factor }" + + termval = self.factor() + while self._accept('TIMES') or self._accept('DIVIDE'): + op = self.tok.type + right = self.factor() + if op == 'TIMES': + termval = ('*', termval, right) + elif op == 'DIVIDE': + termval = ('/', termval, right) + return termval + + def factor(self): + 'factor ::= NUM | ( expr )' + + if self._accept('NUM'): + return int(self.tok.value) + elif self._accept('LPAREN'): + exprval = self.expr() + self._expect('RPAREN') + return exprval + else: + raise SyntaxError('Expected NUMBER or LPAREN') + +if __name__ == '__main__': + e = ExpressionTreeBuilder() + print(e.parse('2 + 3')) + print(e.parse('2 + 3 * 4')) + print(e.parse('2 + (3 + 4) * 5')) + print(e.parse('2 + 3 + 4')) diff --git a/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py new file mode 100644 index 0000000..6d1ee3b --- /dev/null +++ b/chef/cookbooks/python/src/2/writing_a_simple_recursive_descent_parser/plyexample.py @@ -0,0 +1,90 @@ +# plyexample.py +# +# Example of parsing with PLY + +from ply.lex import lex +from ply.yacc import yacc + +# Token list +tokens = [ 'NUM', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN' ] + +# Ignored characters + +t_ignore = ' \t\n' + +# Token specifications (as regexs) +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_LPAREN = r'\(' +t_RPAREN = r'\)' + +# Token processing functions +def t_NUM(t): + r'\d+' + t.value = int(t.value) + return t + +# Error handler +def t_error(t): + print('Bad character: {!r}'.format(t.value[0])) + t.skip(1) + +# Build the lexer +lexer = lex() + +# Grammar rules and handler functions +def p_expr(p): + ''' + expr : expr PLUS term + | expr MINUS term + ''' + if p[2] == '+': + p[0] = p[1] + p[3] + elif p[2] == '-': + p[0] = p[1] - p[3] + +def p_expr_term(p): + ''' + expr : term + ''' + p[0] = p[1] + +def p_term(p): + ''' + term : term TIMES factor + | term DIVIDE factor + ''' + if p[2] == '*': + p[0] = p[1] * p[3] + elif p[2] == '/': + p[0] = p[1] / p[3] + +def p_term_factor(p): + ''' + term : factor + ''' + p[0] = p[1] + +def p_factor(p): + ''' + factor : NUM + ''' + p[0] = p[1] + +def p_factor_group(p): + ''' + factor : LPAREN expr RPAREN + ''' + p[0] = p[2] + +def p_error(p): + print('Syntax error') + +parser = yacc() + +if __name__ == '__main__': + print(parser.parse('2')) + print(parser.parse('2+3')) + print(parser.parse('2+(3+4)*5')) diff --git a/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py b/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py new file mode 100644 index 0000000..b4d6ebc --- /dev/null +++ b/chef/cookbooks/python/src/3/determining_last_fridays_date/example.py @@ -0,0 +1,15 @@ +from datetime import datetime, timedelta + +weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + +def get_previous_byday(dayname, start_date=None): + if start_date is None: + start_date = datetime.today() + day_num = start_date.weekday() + day_num_target = weekdays.index(dayname) + days_ago = (7 + day_num - day_num_target) % 7 + if days_ago == 0: + days_ago = 7 + target_date = start_date - timedelta(days=days_ago) + return target_date + diff --git a/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py b/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py new file mode 100644 index 0000000..2927384 --- /dev/null +++ b/chef/cookbooks/python/src/3/finding_the_date_range_for_the_current_month/example.py @@ -0,0 +1,27 @@ +from datetime import datetime, date, timedelta +import calendar + +def get_month_range(start_date=None): + if start_date is None: + start_date = date.today().replace(day=1) + days_in_month = calendar.monthrange(start_date.year, start_date.month)[1] + end_date = start_date + timedelta(days=days_in_month) + return (start_date, end_date) + +first_day, last_day = get_month_range() +a_day = timedelta(days=1) +while first_day < last_day: + print(first_day) + first_day += a_day + +def daterange(start, stop, step): + while start < stop: + yield start + start += step + +for d in daterange(date(2012, 8, 1), date(2012, 8, 11), timedelta(days=1)): + print(d) + +for d in daterange(datetime(2012, 8, 1), datetime(2012, 8, 3), timedelta(minutes=30)): + print(d) + diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py new file mode 100644 index 0000000..a7ff39a --- /dev/null +++ b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/example.py @@ -0,0 +1,63 @@ +import os +import fnmatch +import gzip +import bz2 +import re + +def gen_find(filepat, top): + ''' + Find all filenames in a directory tree that match a shell wildcard pattern + ''' + for path, dirlist, filelist in os.walk(top): + for name in fnmatch.filter(filelist, filepat): + yield os.path.join(path,name) + +def gen_opener(filenames): + ''' + Open a sequence of filenames one at a time producing a file object. + The file is closed immediately when proceeding to the next iteration. + ''' + for filename in filenames: + if filename.endswith('.gz'): + f = gzip.open(filename, 'rt') + elif filename.endswith('.bz2'): + f = bz2.open(filename, 'rt') + else: + f = open(filename, 'rt') + yield f + f.close() + +def gen_concatenate(iterators): + ''' + Chain a sequence of iterators together into a single sequence. + ''' + for it in iterators: + yield from it + +def gen_grep(pattern, lines): + ''' + Look for a regex pattern in a sequence of lines + ''' + pat = re.compile(pattern) + for line in lines: + if pat.search(line): + yield line + +if __name__ == '__main__': + + # Example 1 + lognames = gen_find('access-log*', 'www') + files = gen_opener(lognames) + lines = gen_concatenate(files) + pylines = gen_grep('(?i)python', lines) + for line in pylines: + print(line) + + # Example 2 + lognames = gen_find('access-log*', 'www') + files = gen_opener(lognames) + lines = gen_concatenate(files) + pylines = gen_grep('(?i)python', lines) + bytecolumn = (line.rsplit(None,1)[1] for line in pylines) + bytes = (int(x) for x in bytecolumn if x != '-') + print('Total', sum(bytes)) diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log new file mode 100644 index 0000000..deeb937 --- /dev/null +++ b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log @@ -0,0 +1,7298 @@ +140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.54.118.139 - - [24/Feb/2008:00:15:40 -0600] "GET / HTTP/1.1" 200 4447 +75.54.118.139 - - [24/Feb/2008:00:15:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +75.54.118.139 - - [24/Feb/2008:00:15:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.54.118.139 - - [24/Feb/2008:00:15:49 -0600] "GET /software.html HTTP/1.1" 200 3163 +75.54.118.139 - - [24/Feb/2008:00:16:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +75.54.118.139 - - [24/Feb/2008:00:16:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.145.165.82 - - [24/Feb/2008:00:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.83 - - [24/Feb/2008:00:31:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.83 - - [24/Feb/2008:00:31:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:38:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.144 - - [24/Feb/2008:00:48:16 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 3110734 +74.6.7.122 - - [24/Feb/2008:00:56:36 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1095 +125.25.238.64 - - [24/Feb/2008:01:04:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +125.25.238.64 - - [24/Feb/2008:01:04:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12382 +116.94.207.182 - - [24/Feb/2008:01:09:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +116.94.207.182 - - [24/Feb/2008:01:10:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +116.94.207.182 - - [24/Feb/2008:01:10:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.136.157 - - [24/Feb/2008:01:13:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.230 - - [24/Feb/2008:01:33:09 -0600] "GET /photos/u505/pages/IMG_1508.htm HTTP/1.0" 404 133 +128.143.38.83 - - [24/Feb/2008:01:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.83 - - [24/Feb/2008:01:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.179.114.61 - - [24/Feb/2008:01:55:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.179.114.61 - - [24/Feb/2008:01:56:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.8.73 - - [24/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +217.136.207.156 - - [24/Feb/2008:02:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.136.207.156 - - [24/Feb/2008:02:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:11 -0600] "GET /ply/example.html HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.136.207.156 - - [24/Feb/2008:02:15:20 -0600] "HEAD /ply/PLYTalk.pdf HTTP/1.1" 200 0 +217.136.207.156 - - [24/Feb/2008:02:15:40 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +77.81.4.30 - - [24/Feb/2008:02:17:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [24/Feb/2008:02:20:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:22:08 -0600] "GET /ply/example.html HTTP/1.1" 304 - +89.182.136.236 - - [24/Feb/2008:02:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:02:23:29 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +117.198.144.124 - - [24/Feb/2008:02:23:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +117.198.144.124 - - [24/Feb/2008:02:23:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:02:24:41 -0600] "GET / HTTP/1.1" 304 - +66.249.65.37 - - [24/Feb/2008:02:26:21 -0600] "GET /index.html HTTP/1.1" 304 - +66.249.65.37 - - [24/Feb/2008:02:26:54 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.232.113.62 - - [24/Feb/2008:02:29:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +200.133.15.2 - - [24/Feb/2008:02:29:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.165.49.150 - - [24/Feb/2008:02:30:00 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1374 +75.165.49.150 - - [24/Feb/2008:02:30:06 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 +74.6.19.156 - - [24/Feb/2008:02:30:21 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 +77.81.4.30 - - [24/Feb/2008:02:34:50 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.8.73 - - [24/Feb/2008:02:39:13 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +189.141.19.88 - - [24/Feb/2008:02:49:01 -0600] "GET /ply/ HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:02:49:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:02:49:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.249.65.37 - - [24/Feb/2008:02:53:59 -0600] "GET /python.html HTTP/1.1" 304 - +131.107.0.112 - - [24/Feb/2008:03:02:22 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +217.196.43.134 - - [24/Feb/2008:03:05:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.100.2.3 - - [24/Feb/2008:03:05:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.100.2.3 - - [24/Feb/2008:03:05:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.8.73 - - [24/Feb/2008:03:09:17 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +88.179.52.81 - - [24/Feb/2008:03:14:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.179.52.81 - - [24/Feb/2008:03:14:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.8.73 - - [24/Feb/2008:03:34:07 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +64.81.229.55 - - [24/Feb/2008:03:49:00 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +64.81.229.55 - - [24/Feb/2008:03:49:08 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +64.81.229.55 - - [24/Feb/2008:03:49:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +58.120.219.129 - - [24/Feb/2008:04:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.120.219.129 - - [24/Feb/2008:04:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.120.219.129 - - [24/Feb/2008:04:03:01 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +58.120.219.129 - - [24/Feb/2008:04:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +65.55.208.119 - - [24/Feb/2008:04:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [24/Feb/2008:04:04:59 -0600] "GET /about.html HTTP/1.1" 200 7890 +74.6.25.20 - - [24/Feb/2008:04:19:14 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.24.37 - - [24/Feb/2008:04:19:14 -0600] "GET /photos/wind/pages/IMG_1255.htm HTTP/1.0" 404 133 +65.55.208.123 - - [24/Feb/2008:04:27:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.123 - - [24/Feb/2008:04:27:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.1" 304 - +86.129.156.19 - - [24/Feb/2008:04:31:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +83.8.193.46 - - [24/Feb/2008:04:31:58 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.8.193.46 - - [24/Feb/2008:04:32:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.66.109.148 - - [24/Feb/2008:04:37:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.53.254.51 - - [24/Feb/2008:04:37:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +79.66.109.148 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.66.109.148 - - [24/Feb/2008:04:38:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +79.66.109.148 - - [24/Feb/2008:04:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +79.66.109.148 - - [24/Feb/2008:04:38:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +83.53.254.51 - - [24/Feb/2008:04:39:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.230.94.215 - - [24/Feb/2008:04:45:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +61.230.94.215 - - [24/Feb/2008:04:45:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:55 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +61.230.94.215 - - [24/Feb/2008:04:47:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:47:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +61.230.94.215 - - [24/Feb/2008:04:48:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +61.230.94.215 - - [24/Feb/2008:04:51:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.241.141.161 - - [24/Feb/2008:05:01:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.241.141.161 - - [24/Feb/2008:05:01:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.232.113.194 - - [24/Feb/2008:05:01:44 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +80.227.1.100 - - [24/Feb/2008:05:01:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +58.176.3.7 - - [24/Feb/2008:05:01:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +60.28.31.194 - - [24/Feb/2008:05:02:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +172.207.178.122 - - [24/Feb/2008:05:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.99.116.176 - - [24/Feb/2008:05:07:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.99.116.176 - - [24/Feb/2008:05:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [24/Feb/2008:05:28:58 -0600] "GET /ply/ HTTP/1.0" 304 - +86.157.119.197 - - [24/Feb/2008:05:35:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.202.49.172 - - [24/Feb/2008:05:37:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [24/Feb/2008:05:37:27 -0600] "GET / HTTP/1.1" 200 4447 +66.116.72.114 - - [24/Feb/2008:05:40:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:05:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:05:42:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 +206.51.237.114 - - [24/Feb/2008:05:49:52 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +200.65.127.161 - - [24/Feb/2008:05:49:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +145.76.18.22 - - [24/Feb/2008:05:49:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +222.122.236.43 - - [24/Feb/2008:05:51:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.44.107 - - [24/Feb/2008:06:00:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +86.157.119.197 - - [24/Feb/2008:06:00:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.8.73 - - [24/Feb/2008:06:14:20 -0600] "GET /training.html HTTP/1.0" 200 6154 +74.6.27.115 - - [24/Feb/2008:06:15:19 -0600] "GET /photos/wind/pages/IMG_1327.htm HTTP/1.0" 404 133 +72.30.226.134 - - [24/Feb/2008:06:20:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +84.110.188.195 - - [24/Feb/2008:06:23:38 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.188.195 - - [24/Feb/2008:06:23:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +67.195.44.109 - - [24/Feb/2008:06:27:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 +61.135.219.5 - - [24/Feb/2008:06:28:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +74.6.8.73 - - [24/Feb/2008:06:32:05 -0600] "GET /photos/wind/pages/IMG_1277.htm HTTP/1.0" 404 133 +84.110.187.127 - - [24/Feb/2008:06:52:46 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.127 - - [24/Feb/2008:06:52:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +66.249.65.37 - - [24/Feb/2008:06:56:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.1" 200 1279 +66.249.65.37 - - [24/Feb/2008:06:57:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.1" 200 1231 +66.249.65.37 - - [24/Feb/2008:07:01:40 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE092.HTM HTTP/1.1" 200 1329 +65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.249.65.37 - - [24/Feb/2008:07:03:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.1" 200 1322 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12238 +89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.49.130.55 - - [24/Feb/2008:07:04:15 -0600] "GET /ply/README HTTP/1.1" 200 8605 +125.99.164.76 - - [24/Feb/2008:07:11:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.249.65.37 - - [24/Feb/2008:07:14:19 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE050.HTM HTTP/1.1" 200 982 +84.110.206.219 - - [24/Feb/2008:07:15:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.206.219 - - [24/Feb/2008:07:15:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1047 +84.110.117.89 - - [24/Feb/2008:07:15:34 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.117.89 - - [24/Feb/2008:07:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1101 +84.110.189.175 - - [24/Feb/2008:07:15:55 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.175 - - [24/Feb/2008:07:15:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1158 +89.49.130.55 - - [24/Feb/2008:07:20:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.110.121.137 - - [24/Feb/2008:07:36:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.121.137 - - [24/Feb/2008:07:36:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1107 +80.93.56.177 - - [24/Feb/2008:07:57:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.93.56.116 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.177 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.116 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.177 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 +66.232.113.62 - - [24/Feb/2008:08:03:16 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2754 +83.229.21.4 - - [24/Feb/2008:08:03:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +219.136.206.29 - - [24/Feb/2008:08:03:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +83.8.193.46 - - [24/Feb/2008:08:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.196.97.157 - - [24/Feb/2008:08:20:04 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:05 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 +79.182.112.231 - - [24/Feb/2008:08:27:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.226.58.44 - - [24/Feb/2008:08:28:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.121.132.53 - - [24/Feb/2008:08:28:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.226.58.44 - - [24/Feb/2008:08:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.226.58.44 - - [24/Feb/2008:08:28:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +82.226.58.44 - - [24/Feb/2008:08:28:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +82.226.58.44 - - [24/Feb/2008:08:28:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 +82.226.58.44 - - [24/Feb/2008:08:29:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.226.58.44 - - [24/Feb/2008:08:29:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +82.226.58.44 - - [24/Feb/2008:08:29:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +88.191.19.81 - - [24/Feb/2008:08:30:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.29.115.55 - - [24/Feb/2008:08:31:57 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.117 - - [24/Feb/2008:08:36:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [24/Feb/2008:08:36:42 -0600] "GET /swill/exec.html HTTP/1.1" 304 - +59.92.203.99 - - [24/Feb/2008:08:42:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.92.203.99 - - [24/Feb/2008:08:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:47:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +79.182.112.231 - - [24/Feb/2008:08:47:29 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +79.182.112.231 - - [24/Feb/2008:08:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +79.182.112.231 - - [24/Feb/2008:08:47:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +79.182.112.231 - - [24/Feb/2008:08:47:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +79.182.112.231 - - [24/Feb/2008:08:48:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +79.182.112.231 - - [24/Feb/2008:08:48:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +189.70.147.197 - - [24/Feb/2008:09:08:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.129.99.56 - - [24/Feb/2008:09:08:05 -0600] "GET /ply/ HTTP/1.1" 304 - +189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:08:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:08:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:12:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:09:17:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:55 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:18:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:18:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:18:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.184.6.249 - - [24/Feb/2008:09:18:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.184.6.249 - - [24/Feb/2008:09:18:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:19:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:20:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:21:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +150.210.155.167 - - [24/Feb/2008:09:22:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /photos/u505/pages/IMG_1484.htm HTTP/1.1" 404 133 +84.122.84.241 - - [24/Feb/2008:09:32:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.122.84.241 - - [24/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.122.84.241 - - [24/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +118.83.147.252 - - [24/Feb/2008:09:35:15 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 +201.21.24.169 - - [24/Feb/2008:09:35:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:37:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.142 - - [24/Feb/2008:09:37:32 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 +189.13.155.147 - - [24/Feb/2008:09:38:31 -0600] "GET / HTTP/1.0" 200 4447 +189.13.155.147 - - [24/Feb/2008:09:38:32 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/ HTTP/1.1" 304 - +189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +67.176.147.11 - - [24/Feb/2008:09:40:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +67.176.147.11 - - [24/Feb/2008:09:40:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +74.6.20.36 - - [24/Feb/2008:09:41:59 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.0" 200 399 +66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 +66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +220.133.118.213 - - [24/Feb/2008:09:46:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.21.24.169 - - [24/Feb/2008:09:48:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:48:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:22 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [24/Feb/2008:09:54:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +80.68.93.199 - - [24/Feb/2008:09:55:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 +80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.68.93.199 - - [24/Feb/2008:09:56:26 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.23.14 - - [24/Feb/2008:09:57:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 +189.70.147.197 - - [24/Feb/2008:09:57:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:09:58:50 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.38.123 - - [24/Feb/2008:09:58:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:59:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:10:01:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:10:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.8.73 - - [24/Feb/2008:10:02:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.110.186.118 - - [24/Feb/2008:10:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.186.118 - - [24/Feb/2008:10:03:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +217.196.43.134 - - [24/Feb/2008:10:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.116.72.114 - - [24/Feb/2008:10:06:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:10:06:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.203.175.16 - - [24/Feb/2008:10:12:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.25.105 - - [24/Feb/2008:10:13:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +128.143.38.123 - - [24/Feb/2008:10:31:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.32 - - [24/Feb/2008:10:31:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.8.73 - - [24/Feb/2008:10:34:02 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +122.55.52.10 - - [24/Feb/2008:10:40:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +122.55.52.10 - - [24/Feb/2008:10:40:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +122.55.52.10 - - [24/Feb/2008:10:40:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +122.55.52.10 - - [24/Feb/2008:10:41:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +189.13.184.120 - - [24/Feb/2008:10:42:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.184.120 - - [24/Feb/2008:10:42:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +122.55.52.10 - - [24/Feb/2008:10:42:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.0" 200 3594 +189.13.184.120 - - [24/Feb/2008:10:44:41 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +88.191.19.81 - - [24/Feb/2008:10:46:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.8.73 - - [24/Feb/2008:10:56:58 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 +61.135.166.102 - - [24/Feb/2008:11:04:25 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.1" 304 - +86.157.4.107 - - [24/Feb/2008:11:24:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.104.157 - - [24/Feb/2008:11:25:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +189.29.245.80 - - [24/Feb/2008:11:25:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.29.245.80 - - [24/Feb/2008:11:25:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 15602 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.169.3 - - [24/Feb/2008:11:26:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.29.245.80 - - [24/Feb/2008:11:26:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.99.169.3 - - [24/Feb/2008:11:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.124.22.177 - - [24/Feb/2008:11:35:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:11:47:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:11:47:12 -0600] "GET /dynamic/ HTTP/1.0" 200 5105 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.199.195 - - [24/Feb/2008:11:54:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.194 - - [24/Feb/2008:11:59:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.71.34.82 - - [24/Feb/2008:12:02:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.71.34.82 - - [24/Feb/2008:12:02:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +83.71.34.82 - - [24/Feb/2008:12:02:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 +67.195.58.174 - - [24/Feb/2008:12:09:31 -0600] "GET /ply/ HTTP/1.0" 304 - +72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.240.122.140 - - [24/Feb/2008:12:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +72.240.122.140 - - [24/Feb/2008:12:19:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.117.28.14 - - [24/Feb/2008:12:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:12:28:47 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +24.1.247.118 - - [24/Feb/2008:12:29:32 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +24.1.247.118 - - [24/Feb/2008:12:29:37 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 731143 +98.206.164.173 - - [24/Feb/2008:12:33:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +98.206.164.173 - - [24/Feb/2008:12:33:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:58 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1878 +76.114.65.194 - - [24/Feb/2008:12:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +76.114.65.194 - - [24/Feb/2008:12:38:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +85.242.185.241 - - [24/Feb/2008:12:41:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.242.185.241 - - [24/Feb/2008:12:41:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:41:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.242.185.241 - - [24/Feb/2008:12:41:58 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +85.242.185.241 - - [24/Feb/2008:12:41:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.10.100.10 - - [24/Feb/2008:12:42:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:12:44:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.195 - - [24/Feb/2008:12:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.137 - - [24/Feb/2008:12:50:05 -0600] "GET /photos/u505/pages/IMG_1500.htm HTTP/1.0" 404 133 +128.143.231.195 - - [24/Feb/2008:12:53:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:56:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:58:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:59:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.166.102 - - [24/Feb/2008:13:04:29 -0600] "GET / HTTP/1.1" 200 4447 +85.155.44.41 - - [24/Feb/2008:13:06:45 -0600] "GET /ply/ HTTP/1.1" 304 - +88.191.19.81 - - [24/Feb/2008:13:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.103.107 - - [24/Feb/2008:13:10:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +99.167.103.107 - - [24/Feb/2008:13:10:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.103.107 - - [24/Feb/2008:13:10:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.60.232.105 - - [24/Feb/2008:13:10:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.60.232.105 - - [24/Feb/2008:13:11:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.42.166.20 - - [24/Feb/2008:13:13:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +201.42.166.20 - - [24/Feb/2008:13:13:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:13:14:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:13:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 126926 +128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +67.195.58.177 - - [24/Feb/2008:13:20:46 -0600] "GET /python.html HTTP/1.0" 304 - +217.219.18.80 - - [24/Feb/2008:13:22:10 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +83.130.163.210 - - [24/Feb/2008:13:23:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.130.163.210 - - [24/Feb/2008:13:23:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:13:24:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +128.143.38.123 - - [24/Feb/2008:13:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.28 - - [24/Feb/2008:13:24:50 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1299 +76.114.65.194 - - [24/Feb/2008:13:25:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +76.114.65.194 - - [24/Feb/2008:13:25:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +76.114.65.194 - - [24/Feb/2008:13:26:19 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +76.114.65.194 - - [24/Feb/2008:13:26:35 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +76.114.65.194 - - [24/Feb/2008:13:27:21 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 +86.157.119.197 - - [24/Feb/2008:13:27:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.80.194.91 - - [24/Feb/2008:13:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.209.71.13 - - [24/Feb/2008:13:32:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.141.81.60 - - [24/Feb/2008:13:32:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.81.60 - - [24/Feb/2008:13:33:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +201.141.81.60 - - [24/Feb/2008:13:33:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.151 - - [24/Feb/2008:13:35:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +98.206.164.173 - - [24/Feb/2008:13:39:37 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +74.6.22.143 - - [24/Feb/2008:13:45:25 -0600] "GET /writing.html HTTP/1.0" 200 2871 +66.232.113.62 - - [24/Feb/2008:13:46:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +80.227.1.100 - - [24/Feb/2008:13:46:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +200.51.41.29 - - [24/Feb/2008:13:46:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +201.42.166.20 - - [24/Feb/2008:13:46:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +201.42.166.20 - - [24/Feb/2008:13:46:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +201.42.166.20 - - [24/Feb/2008:13:46:58 -0600] "GET /cgi-bin/wiki.pl?FormattingRules HTTP/1.1" 200 10503 +201.42.166.20 - - [24/Feb/2008:13:46:59 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +201.42.166.20 - - [24/Feb/2008:13:47:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.64.107.172 - - [24/Feb/2008:13:48:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +190.64.107.172 - - [24/Feb/2008:13:48:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.58.205.41 - - [24/Feb/2008:13:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.58.205.41 - - [24/Feb/2008:13:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.41 - - [24/Feb/2008:13:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +74.6.25.20 - - [24/Feb/2008:13:54:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.143.231.195 - - [24/Feb/2008:13:54:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.168.250.62 - - [24/Feb/2008:13:57:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +70.168.250.62 - - [24/Feb/2008:13:57:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 991 +70.168.250.62 - - [24/Feb/2008:13:57:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1237 +70.168.250.62 - - [24/Feb/2008:13:57:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +69.209.71.13 - - [24/Feb/2008:13:59:17 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +69.209.71.13 - - [24/Feb/2008:13:59:25 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +70.168.250.62 - - [24/Feb/2008:13:59:30 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 +69.209.71.13 - - [24/Feb/2008:13:59:46 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +69.209.71.13 - - [24/Feb/2008:14:00:02 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.24.206.144 - - [24/Feb/2008:14:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.168.250.62 - - [24/Feb/2008:14:04:39 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1564 +70.168.250.62 - - [24/Feb/2008:14:04:42 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +74.6.19.115 - - [24/Feb/2008:14:04:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply/ HTTP/1.0" 200 8018 +58.24.206.144 - - [24/Feb/2008:14:09:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.24.206.144 - - [24/Feb/2008:14:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +58.24.206.144 - - [24/Feb/2008:14:09:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +24.1.247.118 - - [24/Feb/2008:14:09:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.173.185.186 - - [24/Feb/2008:14:15:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +67.173.185.186 - - [24/Feb/2008:14:16:00 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:14:21:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.123 - - [24/Feb/2008:14:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.255.203.160 - - [24/Feb/2008:14:21:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.255.203.160 - - [24/Feb/2008:14:23:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.178 - - [24/Feb/2008:14:26:26 -0600] "GET /software.html HTTP/1.0" 304 - +67.195.58.164 - - [24/Feb/2008:14:27:22 -0600] "GET /dynamic/index.html HTTP/1.0" 304 - +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:30:27 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/ HTTP/1.1" 304 - +199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +199.111.229.93 - - [24/Feb/2008:14:34:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:14:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.172 - - [24/Feb/2008:14:40:05 -0600] "GET /index.html HTTP/1.0" 200 4447 +65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /images/superboard.jpg HTTP/1.0" 200 71119 +204.111.252.233 - - [24/Feb/2008:14:42:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.168 - - [24/Feb/2008:14:46:46 -0600] "GET /consulting.html HTTP/1.0" 304 - +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:14:57:30 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +98.193.69.179 - - [24/Feb/2008:14:58:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +189.13.184.120 - - [24/Feb/2008:14:59:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.173.185.186 - - [24/Feb/2008:15:00:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.166.102 - - [24/Feb/2008:15:04:25 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [24/Feb/2008:15:04:57 -0600] "GET / HTTP/1.1" 200 4447 +217.237.150.206 - - [24/Feb/2008:15:06:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +217.237.150.208 - - [24/Feb/2008:15:06:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.237.150.208 - - [24/Feb/2008:15:06:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 963 +217.237.150.207 - - [24/Feb/2008:15:06:21 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +67.173.185.186 - - [24/Feb/2008:15:10:17 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 313896 +67.173.185.186 - - [24/Feb/2008:15:10:18 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 206 665908 +210.212.55.3 - - [24/Feb/2008:15:18:15 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.25.124 - - [24/Feb/2008:15:18:35 -0600] "GET /photos/u505/pages/IMG_1524.htm HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply HTTP/1.1" 301 242 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 206 1042 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.212.55.3 - - [24/Feb/2008:15:25:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.54.165.36 - - [24/Feb/2008:15:37:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.165.36 - - [24/Feb/2008:15:37:17 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - +69.46.29.140 - - [24/Feb/2008:15:40:50 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 +91.121.92.62 - - [24/Feb/2008:15:40:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +206.51.237.114 - - [24/Feb/2008:15:41:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +88.255.192.42 - - [24/Feb/2008:15:41:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +83.229.21.4 - - [24/Feb/2008:15:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +85.214.114.155 - - [24/Feb/2008:15:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +210.22.158.132 - - [24/Feb/2008:15:41:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +66.232.113.194 - - [24/Feb/2008:15:43:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +60.234.20.98 - - [24/Feb/2008:15:43:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.216.186.35 - - [24/Feb/2008:15:43:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply HTTP/1.1" 301 242 +86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [24/Feb/2008:15:56:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [24/Feb/2008:15:56:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [24/Feb/2008:16:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.199.136 - - [24/Feb/2008:16:05:51 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.199.136 - - [24/Feb/2008:16:05:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1149 +89.229.25.165 - - [24/Feb/2008:16:06:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.229.25.165 - - [24/Feb/2008:16:06:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:07:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +75.32.37.138 - - [24/Feb/2008:16:12:29 -0600] "GET / HTTP/1.1" 200 4447 +75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [24/Feb/2008:16:13:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +75.32.37.138 - - [24/Feb/2008:16:13:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +204.111.252.233 - - [24/Feb/2008:16:14:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [24/Feb/2008:16:15:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +85.185.11.131 - - [24/Feb/2008:16:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +64.124.150.55 - - [24/Feb/2008:16:15:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET / HTTP/1.1" 200 4447 +128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +172.159.46.8 - - [24/Feb/2008:16:21:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +172.159.46.8 - - [24/Feb/2008:16:21:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +172.159.46.8 - - [24/Feb/2008:16:21:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:21:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [24/Feb/2008:16:25:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [24/Feb/2008:16:25:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.176.224.242 - - [24/Feb/2008:16:30:34 -0600] "GET /robots.txt HTTP/1.0" 200 71 +89.229.25.165 - - [24/Feb/2008:16:33:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.89.137.229 - - [24/Feb/2008:16:33:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.89.137.229 - - [24/Feb/2008:16:33:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.6.151.80 - - [24/Feb/2008:16:39:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.11.245 - - [24/Feb/2008:16:40:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.4.230.43 - - [24/Feb/2008:16:40:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +64.4.230.43 - - [24/Feb/2008:16:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.4.230.43 - - [24/Feb/2008:16:40:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +74.6.19.101 - - [24/Feb/2008:16:46:54 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 +67.173.185.186 - - [24/Feb/2008:16:48:30 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.62.148.145 - - [24/Feb/2008:16:56:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.62.148.145 - - [24/Feb/2008:16:56:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:56:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:56:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:57:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.181.38.169 - - [24/Feb/2008:17:04:19 -0600] "GET / HTTP/1.1" 200 4447 +61.135.166.102 - - [24/Feb/2008:17:04:25 -0600] "GET / HTTP/1.1" 200 4447 +217.196.43.134 - - [24/Feb/2008:17:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.17 - - [24/Feb/2008:17:07:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.17 - - [24/Feb/2008:17:07:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.205.10 - - [24/Feb/2008:17:10:10 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.202.87.9 - - [24/Feb/2008:17:12:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +199.111.205.10 - - [24/Feb/2008:17:14:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.62.148.145 - - [24/Feb/2008:17:18:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:19:36 -0600] "GET /ply/README HTTP/1.1" 200 8605 +71.62.148.145 - - [24/Feb/2008:17:21:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +199.111.205.10 - - [24/Feb/2008:17:22:18 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.248.57.218 - - [24/Feb/2008:17:24:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:26:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +69.209.71.13 - - [24/Feb/2008:17:26:43 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +71.62.148.145 - - [24/Feb/2008:17:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET / HTTP/1.1" 200 4447 +71.62.148.145 - - [24/Feb/2008:17:37:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:37:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.195.58.182 - - [24/Feb/2008:17:38:07 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +67.195.58.186 - - [24/Feb/2008:17:38:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +67.195.58.170 - - [24/Feb/2008:17:38:32 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +67.195.58.181 - - [24/Feb/2008:17:38:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +67.173.185.186 - - [24/Feb/2008:17:38:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.151 - - [24/Feb/2008:17:39:02 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +189.144.107.121 - - [24/Feb/2008:17:40:22 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +189.144.107.121 - - [24/Feb/2008:17:40:23 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +76.198.207.51 - - [24/Feb/2008:17:44:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:17:47:25 -0600] "GET /python.html HTTP/1.1" 200 18870 +151.33.39.177 - - [24/Feb/2008:17:47:26 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.91.200.98 - - [24/Feb/2008:17:48:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.195.58.164 - - [24/Feb/2008:17:54:04 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +76.10.149.199 - - [24/Feb/2008:17:54:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.10.149.199 - - [24/Feb/2008:17:54:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.10.149.199 - - [24/Feb/2008:17:54:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +72.244.56.83 - - [24/Feb/2008:17:54:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +72.244.56.83 - - [24/Feb/2008:17:54:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.244.56.83 - - [24/Feb/2008:17:55:22 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +65.247.230.214 - - [24/Feb/2008:17:56:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.10.149.199 - - [24/Feb/2008:17:56:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +76.10.149.199 - - [24/Feb/2008:17:56:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +76.10.149.199 - - [24/Feb/2008:17:56:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 +65.55.208.118 - - [24/Feb/2008:17:57:44 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [24/Feb/2008:17:57:45 -0600] "GET /photos/u505/pages/IMG_1530.htm HTTP/1.1" 404 133 +74.6.19.115 - - [24/Feb/2008:17:59:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.145 - - [24/Feb/2008:17:59:40 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +88.191.19.81 - - [24/Feb/2008:18:01:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.22.143 - - [24/Feb/2008:18:02:06 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +71.230.189.170 - - [24/Feb/2008:18:06:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +151.33.39.177 - - [24/Feb/2008:18:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:18:10:37 -0600] "GET /software.html HTTP/1.1" 200 3163 +84.110.216.199 - - [24/Feb/2008:18:17:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.216.199 - - [24/Feb/2008:18:17:33 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 +67.195.58.168 - - [24/Feb/2008:18:17:51 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +64.81.241.54 - - [24/Feb/2008:18:26:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.241.54 - - [24/Feb/2008:18:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [24/Feb/2008:18:32:02 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.146.214.212 - - [24/Feb/2008:18:32:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.206.63.189 - - [24/Feb/2008:18:37:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.206.63.189 - - [24/Feb/2008:18:38:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +82.35.89.141 - - [24/Feb/2008:18:43:23 -0600] "GET /ply/ HTTP/1.1" 304 - +82.35.89.141 - - [24/Feb/2008:18:43:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +82.226.58.44 - - [24/Feb/2008:18:45:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.226.58.44 - - [24/Feb/2008:18:45:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.226.58.44 - - [24/Feb/2008:18:45:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +82.226.58.44 - - [24/Feb/2008:18:46:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +128.143.70.18 - - [24/Feb/2008:18:48:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.110.209.197 - - [24/Feb/2008:18:56:42 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.209.197 - - [24/Feb/2008:18:56:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 +58.215.57.238 - - [24/Feb/2008:18:59:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.215.57.238 - - [24/Feb/2008:18:59:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.215.57.238 - - [24/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET /robots.txt HTTP/1.0" 200 71 +216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET / HTTP/1.1" 206 4447 +218.94.136.173 - - [24/Feb/2008:19:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.94.136.173 - - [24/Feb/2008:19:05:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.94.136.173 - - [24/Feb/2008:19:05:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +74.6.22.143 - - [24/Feb/2008:19:21:03 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 +72.85.134.143 - - [24/Feb/2008:19:23:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.85.134.143 - - [24/Feb/2008:19:25:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.207.163.47 - - [24/Feb/2008:19:30:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:31:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:31:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +210.245.52.8 - - [24/Feb/2008:19:38:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.245.52.8 - - [24/Feb/2008:19:38:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.141.19.88 - - [24/Feb/2008:19:42:42 -0600] "GET /ply/ HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.141.19.88 - - [24/Feb/2008:19:42:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.57.91.136 - - [24/Feb/2008:19:46:56 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.52 - - [24/Feb/2008:19:49:28 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [24/Feb/2008:19:49:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +24.84.190.11 - - [24/Feb/2008:19:50:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +74.6.28.151 - - [24/Feb/2008:19:53:29 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1335 +201.86.78.151 - - [24/Feb/2008:19:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.86.78.151 - - [24/Feb/2008:19:54:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:54:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.190.17 - - [24/Feb/2008:19:58:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.28.162 - - [24/Feb/2008:19:59:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 +99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.184.199 - - [24/Feb/2008:20:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [24/Feb/2008:20:00:49 -0600] "GET /dynamic/ HTTP/1.1" 304 - +75.22.21.146 - - [24/Feb/2008:20:01:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [24/Feb/2008:20:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:04:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +201.141.93.154 - - [24/Feb/2008:20:05:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.141.93.154 - - [24/Feb/2008:20:05:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.141.93.154 - - [24/Feb/2008:20:05:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:06:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:06:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.143.35.13 - - [24/Feb/2008:20:09:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.143.35.13 - - [24/Feb/2008:20:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:13:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:20:21:31 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +189.13.184.120 - - [24/Feb/2008:20:33:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.184.120 - - [24/Feb/2008:20:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.13.184.120 - - [24/Feb/2008:20:44:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.58.174 - - [24/Feb/2008:20:44:24 -0600] "GET /ply/ HTTP/1.0" 304 - +165.82.137.78 - - [24/Feb/2008:20:45:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +67.195.58.160 - - [24/Feb/2008:20:45:23 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +67.195.58.174 - - [24/Feb/2008:20:45:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +67.195.58.178 - - [24/Feb/2008:20:46:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +67.186.98.20 - - [24/Feb/2008:20:48:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +67.186.98.20 - - [24/Feb/2008:20:48:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [24/Feb/2008:20:48:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.107.0.112 - - [24/Feb/2008:20:54:44 -0600] "GET /robots.txt HTTP/1.1" 304 - +131.107.0.112 - - [24/Feb/2008:20:55:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +220.237.12.253 - - [24/Feb/2008:20:56:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:58:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:59:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.176.147.11 - - [24/Feb/2008:20:59:21 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +189.13.184.120 - - [24/Feb/2008:21:01:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +165.82.137.78 - - [24/Feb/2008:21:02:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +165.82.137.78 - - [24/Feb/2008:21:02:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +165.82.137.78 - - [24/Feb/2008:21:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +61.135.166.102 - - [24/Feb/2008:21:04:22 -0600] "GET / HTTP/1.1" 200 4447 +220.237.12.253 - - [24/Feb/2008:21:04:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:21:04:29 -0600] "GET /swill/writing.html HTTP/1.0" 404 133 +220.237.12.253 - - [24/Feb/2008:21:05:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +68.72.123.219 - - [24/Feb/2008:21:06:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +125.16.133.35 - - [24/Feb/2008:21:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [24/Feb/2008:21:08:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:21:10:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +165.82.137.78 - - [24/Feb/2008:21:10:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +165.82.137.78 - - [24/Feb/2008:21:10:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +83.249.251.158 - - [24/Feb/2008:21:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.249.251.158 - - [24/Feb/2008:21:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [24/Feb/2008:21:11:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.72.123.219 - - [24/Feb/2008:21:12:25 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +68.72.123.219 - - [24/Feb/2008:21:12:32 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +74.6.20.207 - - [24/Feb/2008:21:18:07 -0600] "GET /writing.html HTTP/1.0" 200 2871 +220.237.12.253 - - [24/Feb/2008:21:28:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:30:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +202.181.80.140 - - [24/Feb/2008:21:34:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +202.181.80.140 - - [24/Feb/2008:21:34:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.181.80.140 - - [24/Feb/2008:21:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +202.181.80.140 - - [24/Feb/2008:21:35:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +139.175.68.252 - - [24/Feb/2008:21:40:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +88.191.19.81 - - [24/Feb/2008:21:44:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.219.203.174 - - [24/Feb/2008:21:45:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +86.219.203.174 - - [24/Feb/2008:21:48:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +86.219.203.174 - - [24/Feb/2008:21:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:21:50:37 -0600] "GET /cv.html HTTP/1.0" 200 31798 +67.176.147.11 - - [24/Feb/2008:22:03:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +189.6.242.136 - - [24/Feb/2008:22:12:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.6.242.136 - - [24/Feb/2008:22:12:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.6.242.136 - - [24/Feb/2008:22:15:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [24/Feb/2008:22:20:56 -0600] "GET / HTTP/1.1" 200 4447 +207.229.184.99 - - [24/Feb/2008:22:22:52 -0600] "GET /dynamic/ HTTP/1.1" 304 - +207.229.184.99 - - [24/Feb/2008:22:22:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +74.6.28.156 - - [24/Feb/2008:22:26:48 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +74.6.24.162 - - [24/Feb/2008:22:29:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 +74.6.26.198 - - [24/Feb/2008:22:30:06 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +139.175.68.252 - - [24/Feb/2008:22:33:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:22:33:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:22:33:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.200 - - [24/Feb/2008:22:48:41 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +220.181.38.169 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 +61.135.166.102 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 +68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.34.230.143 - - [24/Feb/2008:23:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +58.211.255.253 - - [24/Feb/2008:23:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.211.255.253 - - [24/Feb/2008:23:11:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.116.72.114 - - [24/Feb/2008:23:13:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:23:13:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.120 - - [24/Feb/2008:23:14:09 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.0" 200 375 +207.229.184.99 - - [24/Feb/2008:23:22:11 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +207.229.184.99 - - [24/Feb/2008:23:22:22 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +74.6.22.143 - - [24/Feb/2008:23:35:43 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1210 +67.186.98.20 - - [24/Feb/2008:23:35:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:39:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:23:40:11 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +139.175.68.252 - - [24/Feb/2008:23:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.247.172.9 - - [24/Feb/2008:23:49:24 -0600] "GET / HTTP/1.1" 200 4447 +41.196.193.85 - - [24/Feb/2008:23:49:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.196.193.85 - - [24/Feb/2008:23:49:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +41.196.193.85 - - [24/Feb/2008:23:49:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.84.154.13 - - [24/Feb/2008:23:50:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.106 - - [24/Feb/2008:23:55:14 -0600] "GET /photos/u505/ HTTP/1.0" 404 133 +217.196.43.134 - - [25/Feb/2008:00:05:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.242.107.51 - - [25/Feb/2008:00:11:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.145.165.82 - - [25/Feb/2008:00:15:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.206.161.230 - - [25/Feb/2008:00:16:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +138.206.161.230 - - [25/Feb/2008:00:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +138.206.161.230 - - [25/Feb/2008:00:16:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +148.243.90.225 - - [25/Feb/2008:00:17:55 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.22.143 - - [25/Feb/2008:00:18:25 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +57.73.25.166 - - [25/Feb/2008:00:22:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:00:24:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [25/Feb/2008:00:24:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +74.6.25.20 - - [25/Feb/2008:00:26:13 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.169 - - [25/Feb/2008:00:26:14 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.127.232.14 - - [25/Feb/2008:00:26:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +211.127.232.14 - - [25/Feb/2008:00:26:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +211.127.232.14 - - [25/Feb/2008:00:28:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +211.127.232.14 - - [25/Feb/2008:00:28:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2813 +211.127.232.14 - - [25/Feb/2008:00:28:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +211.127.232.14 - - [25/Feb/2008:00:29:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 +211.127.232.14 - - [25/Feb/2008:00:29:38 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.192.28.26 - - [25/Feb/2008:00:37:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +71.192.28.26 - - [25/Feb/2008:00:37:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.195.58.175 - - [25/Feb/2008:00:46:01 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +74.6.22.143 - - [25/Feb/2008:00:48:20 -0600] "GET /index.html HTTP/1.0" 200 4447 +88.191.19.81 - - [25/Feb/2008:00:48:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.126.89.51 - - [25/Feb/2008:00:50:17 -0600] "GET / HTTP/1.1" 200 4447 +86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply HTTP/1.1" 301 242 +86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +125.35.5.39 - - [25/Feb/2008:01:02:32 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +125.35.5.39 - - [25/Feb/2008:01:02:33 -0600] "GET /ply/ HTTP/1.0" 200 8018 +125.35.5.39 - - [25/Feb/2008:01:02:34 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +146.230.128.29 - - [25/Feb/2008:01:03:58 -0600] "GET /cv.html HTTP/1.0" 200 31798 +146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.135.166.102 - - [25/Feb/2008:01:04:27 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:01:04:54 -0600] "GET / HTTP/1.1" 200 4447 +146.230.128.29 - - [25/Feb/2008:01:05:20 -0600] "GET / HTTP/1.0" 200 4447 +146.230.128.29 - - [25/Feb/2008:01:05:21 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 +139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +139.175.68.252 - - [25/Feb/2008:01:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +146.230.128.29 - - [25/Feb/2008:01:06:15 -0600] "GET /python.html HTTP/1.0" 200 18870 +146.230.128.29 - - [25/Feb/2008:01:06:16 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +146.230.128.29 - - [25/Feb/2008:01:06:17 -0600] "GET /software.html HTTP/1.0" 200 3163 +146.230.128.29 - - [25/Feb/2008:01:06:33 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply HTTP/1.1" 301 242 +148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.160 - - [25/Feb/2008:01:08:56 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - +71.192.28.26 - - [25/Feb/2008:01:10:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [25/Feb/2008:01:18:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:01:18:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.172.44.82 - - [25/Feb/2008:01:22:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.80.193.48 - - [25/Feb/2008:01:22:20 -0600] "GET / HTTP/1.1" 200 4447 +198.54.202.210 - - [25/Feb/2008:01:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.54.202.194 - - [25/Feb/2008:01:25:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 +70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +70.90.215.85 - - [25/Feb/2008:01:28:54 -0600] "GET /ply HTTP/1.0" 301 230 +70.90.215.85 - - [25/Feb/2008:01:28:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.78.221.48 - - [25/Feb/2008:01:29:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.78.221.48 - - [25/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +138.246.7.155 - - [25/Feb/2008:01:38:12 -0600] "GET /ply/ HTTP/1.1" 304 - +138.246.7.155 - - [25/Feb/2008:01:38:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.185.239.242 - - [25/Feb/2008:01:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +58.185.239.242 - - [25/Feb/2008:01:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.185.239.242 - - [25/Feb/2008:01:39:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +69.137.228.16 - - [25/Feb/2008:01:46:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.185.239.242 - - [25/Feb/2008:01:48:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +58.185.239.242 - - [25/Feb/2008:01:48:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +58.185.239.242 - - [25/Feb/2008:01:48:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +84.177.46.231 - - [25/Feb/2008:01:50:08 -0600] "GET /python.html HTTP/1.1" 200 18870 +84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.177.46.231 - - [25/Feb/2008:01:50:10 -0600] "GET /training.html HTTP/1.1" 200 6154 +206.51.237.114 - - [25/Feb/2008:01:51:23 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +88.255.192.42 - - [25/Feb/2008:01:51:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.247.11.155 - - [25/Feb/2008:01:51:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.65.127.161 - - [25/Feb/2008:01:51:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +71.38.14.119 - - [25/Feb/2008:01:54:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.38.14.119 - - [25/Feb/2008:01:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +69.137.228.16 - - [25/Feb/2008:01:57:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +138.246.7.155 - - [25/Feb/2008:02:01:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.194.30.240 - - [25/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +87.194.30.240 - - [25/Feb/2008:02:04:30 -0600] "GET /ply HTTP/1.1" 301 242 +87.194.30.240 - - [25/Feb/2008:02:04:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +217.237.70.128 - - [25/Feb/2008:02:08:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.237.70.128 - - [25/Feb/2008:02:08:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.6.228.64 - - [25/Feb/2008:02:18:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.6.228.64 - - [25/Feb/2008:02:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.6.228.64 - - [25/Feb/2008:02:18:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +75.6.228.64 - - [25/Feb/2008:02:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.1" 200 3606 +75.6.228.64 - - [25/Feb/2008:02:18:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +66.232.113.194 - - [25/Feb/2008:02:22:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +200.133.15.2 - - [25/Feb/2008:02:22:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +219.93.178.162 - - [25/Feb/2008:02:22:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +217.255.38.100 - - [25/Feb/2008:02:28:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.137.228.16 - - [25/Feb/2008:02:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:36:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.79.100.39 - - [25/Feb/2008:02:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1554 +130.79.100.39 - - [25/Feb/2008:02:41:24 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 +130.79.100.39 - - [25/Feb/2008:02:41:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.79.100.39 - - [25/Feb/2008:02:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +74.6.22.143 - - [25/Feb/2008:02:45:47 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.55.208.122 - - [25/Feb/2008:02:49:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [25/Feb/2008:02:49:52 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.1" 304 - +74.6.23.75 - - [25/Feb/2008:02:52:21 -0600] "GET /photos/u505/pages/IMG_1502.htm HTTP/1.0" 404 133 +217.255.38.100 - - [25/Feb/2008:02:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:54:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +81.255.106.203 - - [25/Feb/2008:02:56:49 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.22.143 - - [25/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +61.135.166.102 - - [25/Feb/2008:03:04:19 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:03:05:28 -0600] "GET / HTTP/1.1" 200 4447 +194.105.57.11 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.105.57.12 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.105.57.12 - - [25/Feb/2008:03:06:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.15 - - [25/Feb/2008:03:11:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.15 - - [25/Feb/2008:03:11:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +74.6.7.107 - - [25/Feb/2008:03:13:02 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.0" 200 288790 +151.96.0.8 - - [25/Feb/2008:03:13:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +151.96.0.8 - - [25/Feb/2008:03:13:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.185.76.213 - - [25/Feb/2008:03:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.185.76.213 - - [25/Feb/2008:03:16:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/ HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +193.190.210.85 - - [25/Feb/2008:03:23:06 -0600] "GET /ply/ HTTP/1.1" 304 - +203.200.35.12 - - [25/Feb/2008:03:28:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.200.35.12 - - [25/Feb/2008:03:28:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.200.35.12 - - [25/Feb/2008:03:28:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.200.35.12 - - [25/Feb/2008:03:28:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/ HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:29:55 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +203.200.218.2 - - [25/Feb/2008:03:30:17 -0600] "GET /python.html HTTP/1.0" 200 18870 +203.200.218.2 - - [25/Feb/2008:03:30:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +203.200.218.2 - - [25/Feb/2008:03:34:35 -0600] "GET /ply/ HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:34:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 +66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.200.218.2 - - [25/Feb/2008:03:34:36 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.54.42 - - [25/Feb/2008:03:34:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET / HTTP/1.1" 200 4447 +66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +203.200.218.2 - - [25/Feb/2008:03:34:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /cv.html HTTP/1.1" 200 31798 +72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.1" 200 1232 +67.195.58.174 - - [25/Feb/2008:03:35:56 -0600] "GET /ply/ HTTP/1.0" 304 - +66.249.65.37 - - [25/Feb/2008:03:36:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 200 1336 +66.249.65.37 - - [25/Feb/2008:03:39:15 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE063.HTM HTTP/1.1" 200 984 +124.30.116.190 - - [25/Feb/2008:03:42:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [25/Feb/2008:03:44:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +124.30.116.190 - - [25/Feb/2008:03:44:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.142.212.161 - - [25/Feb/2008:03:51:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 +68.142.212.161 - - [25/Feb/2008:03:51:39 -0600] "GET /images/BadDave1.jpg HTTP/1.0" 304 - +139.175.68.252 - - [25/Feb/2008:03:53:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.142.212.161 - - [25/Feb/2008:03:55:08 -0600] "GET /images/davechina.jpg HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:59:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.89.228.178 - - [25/Feb/2008:04:33:30 -0600] "GET / HTTP/1.0" 200 4447 +130.225.195.70 - - [25/Feb/2008:04:47:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.225.195.70 - - [25/Feb/2008:04:47:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.126.185 - - [25/Feb/2008:04:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.126.185 - - [25/Feb/2008:04:48:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +61.135.166.102 - - [25/Feb/2008:05:04:21 -0600] "GET / HTTP/1.1" 200 4447 +84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +130.225.195.70 - - [25/Feb/2008:05:12:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.154.251.108 - - [25/Feb/2008:05:13:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.154.251.108 - - [25/Feb/2008:05:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.154.251.108 - - [25/Feb/2008:05:13:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.154.251.108 - - [25/Feb/2008:05:14:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.154.251.108 - - [25/Feb/2008:05:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.154.251.108 - - [25/Feb/2008:05:14:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +74.6.20.35 - - [25/Feb/2008:05:24:35 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1474 +74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /swill/swill-0.1.tar.gz HTTP/1.0" 200 119170 +58.107.212.3 - - [25/Feb/2008:05:34:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.226.57.203 - - [25/Feb/2008:05:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:05:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +74.6.25.148 - - [25/Feb/2008:05:48:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.107.147.45 - - [25/Feb/2008:05:49:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.107.147.45 - - [25/Feb/2008:05:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +82.107.147.45 - - [25/Feb/2008:05:49:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +82.107.147.45 - - [25/Feb/2008:05:49:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +82.107.147.45 - - [25/Feb/2008:05:49:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +88.191.19.81 - - [25/Feb/2008:05:52:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:53:28 -0600] "GET / HTTP/1.1" 200 4447 +58.24.206.144 - - [25/Feb/2008:05:53:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +58.24.206.144 - - [25/Feb/2008:05:54:13 -0600] "GET /python.html HTTP/1.1" 200 18870 +58.24.206.144 - - [25/Feb/2008:05:54:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +58.24.206.144 - - [25/Feb/2008:05:54:34 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:54:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +131.159.46.32 - - [25/Feb/2008:05:54:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:55:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:05:57:56 -0600] "GET /dynamic/sd.html HTTP/1.1" 304 - +58.24.206.144 - - [25/Feb/2008:05:58:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.172.19.20 - - [25/Feb/2008:05:59:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.166.41.113 - - [25/Feb/2008:06:01:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +88.166.41.113 - - [25/Feb/2008:06:02:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:15:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.110.220.12 - - [25/Feb/2008:06:15:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.110.220.12 - - [25/Feb/2008:06:15:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:24:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +92.112.194.106 - - [25/Feb/2008:06:35:05 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET / HTTP/1.1" 200 4447 +125.16.133.35 - - [25/Feb/2008:06:44:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.246.7.155 - - [25/Feb/2008:06:45:59 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +200.21.98.7 - - [25/Feb/2008:06:50:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.21.98.7 - - [25/Feb/2008:06:50:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.240.69.90 - - [25/Feb/2008:06:52:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.240.69.90 - - [25/Feb/2008:06:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.240.69.90 - - [25/Feb/2008:06:52:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.57.248.115 - - [25/Feb/2008:06:55:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.21.126.135 - - [25/Feb/2008:06:55:07 -0600] "GET /ply/ HTTP/1.0" 200 8018 +192.54.144.229 - - [25/Feb/2008:06:55:20 -0600] "GET /ply HTTP/1.1" 301 242 +192.54.144.229 - - [25/Feb/2008:06:55:21 -0600] "GET /ply/ HTTP/1.1" 304 - +80.57.248.115 - - [25/Feb/2008:06:55:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +196.21.126.135 - - [25/Feb/2008:06:55:55 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +124.215.248.26 - - [25/Feb/2008:06:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:57:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:06:58:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +124.215.248.26 - - [25/Feb/2008:07:00:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.181.38.169 - - [25/Feb/2008:07:04:30 -0600] "GET / HTTP/1.1" 200 4447 +84.89.249.77 - - [25/Feb/2008:07:04:44 -0600] "GET /ply/ HTTP/1.1" 304 - +84.89.249.77 - - [25/Feb/2008:07:04:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.196.43.134 - - [25/Feb/2008:07:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.215.248.26 - - [25/Feb/2008:07:05:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:07:07:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.94.82.18 - - [25/Feb/2008:07:10:52 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +140.94.82.18 - - [25/Feb/2008:07:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.255.103.130 - - [25/Feb/2008:07:11:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 +200.255.103.130 - - [25/Feb/2008:07:12:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 408 +200.255.103.130 - - [25/Feb/2008:07:12:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 410 +200.255.103.130 - - [25/Feb/2008:07:12:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 912 +200.255.103.130 - - [25/Feb/2008:07:12:24 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 +140.94.82.18 - - [25/Feb/2008:07:13:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 +130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:15:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.62.75.201 - - [25/Feb/2008:07:16:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.255.103.130 - - [25/Feb/2008:07:18:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET / HTTP/1.1" 200 4447 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:07:24:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +71.57.91.136 - - [25/Feb/2008:07:24:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.19.207 - - [25/Feb/2008:07:24:42 -0600] "GET /photos/wind/pages/IMG_1270.htm HTTP/1.0" 404 133 +82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +192.93.158.26 - - [25/Feb/2008:07:39:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:42:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.221.197.20 - - [25/Feb/2008:07:43:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +124.30.116.190 - - [25/Feb/2008:07:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +74.6.22.150 - - [25/Feb/2008:07:54:27 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 +74.6.26.75 - - [25/Feb/2008:07:55:55 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 +84.237.120.134 - - [25/Feb/2008:07:56:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.195.186.41 - - [25/Feb/2008:07:56:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.195.186.41 - - [25/Feb/2008:07:56:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [25/Feb/2008:08:02:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [25/Feb/2008:08:04:55 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [25/Feb/2008:08:04:56 -0600] "GET / HTTP/1.1" 200 4447 +84.237.120.134 - - [25/Feb/2008:08:05:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +59.124.114.4 - - [25/Feb/2008:08:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +59.124.114.4 - - [25/Feb/2008:08:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +59.124.114.4 - - [25/Feb/2008:08:06:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +71.57.91.136 - - [25/Feb/2008:08:07:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 121887 +71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 +203.109.126.184 - - [25/Feb/2008:08:08:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +203.109.126.184 - - [25/Feb/2008:08:08:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:08:09:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.124.114.4 - - [25/Feb/2008:08:14:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +72.14.220.136 - - [25/Feb/2008:08:15:44 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.195.66.68 - - [25/Feb/2008:08:16:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +72.14.220.136 - - [25/Feb/2008:08:16:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +72.14.220.136 - - [25/Feb/2008:08:16:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +72.14.220.136 - - [25/Feb/2008:08:17:01 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.195.66.68 - - [25/Feb/2008:08:17:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +84.20.132.177 - - [25/Feb/2008:08:18:06 -0600] "GET /ply HTTP/1.1" 301 242 +84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.20.132.177 - - [25/Feb/2008:08:18:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.158 - - [25/Feb/2008:08:18:19 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 +217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.153.4.50 - - [25/Feb/2008:08:18:39 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +217.153.4.50 - - [25/Feb/2008:08:18:40 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +200.195.66.68 - - [25/Feb/2008:08:18:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +217.153.4.50 - - [25/Feb/2008:08:18:44 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +84.20.132.177 - - [25/Feb/2008:08:18:50 -0600] "GET /ply/README HTTP/1.1" 200 8605 +200.195.66.68 - - [25/Feb/2008:08:19:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +200.195.66.68 - - [25/Feb/2008:08:19:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +200.195.66.68 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.0" 200 2290 +217.153.4.50 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 +200.195.66.68 - - [25/Feb/2008:08:19:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.0" 200 4413 +200.195.66.68 - - [25/Feb/2008:08:19:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.0" 200 1641 +200.195.66.68 - - [25/Feb/2008:08:20:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +217.153.4.50 - - [25/Feb/2008:08:20:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 +217.153.4.50 - - [25/Feb/2008:08:20:12 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +66.212.158.132 - - [25/Feb/2008:08:20:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.153.4.50 - - [25/Feb/2008:08:20:57 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 +200.195.66.68 - - [25/Feb/2008:08:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.0" 200 4986 +217.153.4.50 - - [25/Feb/2008:08:21:24 -0600] "GET /cgi-bin/wiki.pl?ConfigurationMemory HTTP/1.1" 200 3793 +217.153.4.50 - - [25/Feb/2008:08:21:40 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +67.195.45.214 - - [25/Feb/2008:08:22:04 -0600] "GET / HTTP/1.0" 200 4447 +200.155.226.207 - - [25/Feb/2008:08:28:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.155.226.207 - - [25/Feb/2008:08:28:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.155.226.207 - - [25/Feb/2008:08:28:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.155.226.207 - - [25/Feb/2008:08:28:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:08:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [25/Feb/2008:08:33:13 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 225488 +64.81.229.55 - - [25/Feb/2008:08:33:14 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246552 +82.195.186.41 - - [25/Feb/2008:08:33:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +82.195.186.41 - - [25/Feb/2008:08:33:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.65.240.234 - - [25/Feb/2008:08:33:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 +217.65.240.234 - - [25/Feb/2008:08:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +216.72.46.162 - - [25/Feb/2008:08:34:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.72.46.162 - - [25/Feb/2008:08:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.72.46.162 - - [25/Feb/2008:08:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [25/Feb/2008:08:35:03 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 +128.221.197.20 - - [25/Feb/2008:08:37:40 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +192.35.17.30 - - [25/Feb/2008:08:37:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.35.17.30 - - [25/Feb/2008:08:38:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +220.227.29.99 - - [25/Feb/2008:08:39:52 -0600] "GET /python.html HTTP/1.0" 200 18870 +220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.227.29.99 - - [25/Feb/2008:08:40:16 -0600] "GET /training.html HTTP/1.0" 200 6154 +220.227.29.99 - - [25/Feb/2008:08:40:24 -0600] "GET /software.html HTTP/1.0" 200 3163 +220.227.29.99 - - [25/Feb/2008:08:40:28 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +59.124.114.4 - - [25/Feb/2008:08:41:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +59.124.114.4 - - [25/Feb/2008:08:42:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +59.124.114.4 - - [25/Feb/2008:08:42:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +66.232.113.62 - - [25/Feb/2008:08:51:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +38.101.222.130 - - [25/Feb/2008:08:51:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +217.172.56.49 - - [25/Feb/2008:08:51:55 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +62.197.78.103 - - [25/Feb/2008:09:01:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.197.78.103 - - [25/Feb/2008:09:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.197.78.103 - - [25/Feb/2008:09:01:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +61.135.166.102 - - [25/Feb/2008:09:04:23 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:09:07:34 -0600] "GET / HTTP/1.1" 200 4447 +132.207.44.190 - - [25/Feb/2008:09:13:55 -0600] "GET /ply/ HTTP/1.1" 304 - +132.207.44.190 - - [25/Feb/2008:09:14:13 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +24.15.187.198 - - [25/Feb/2008:09:19:44 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +24.15.187.198 - - [25/Feb/2008:09:19:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +213.186.249.190 - - [25/Feb/2008:09:20:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.186.249.190 - - [25/Feb/2008:09:20:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.186.249.190 - - [25/Feb/2008:09:20:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.186.249.190 - - [25/Feb/2008:09:20:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.206.100.135 - - [25/Feb/2008:09:22:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +130.206.100.135 - - [25/Feb/2008:09:22:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +130.206.100.135 - - [25/Feb/2008:09:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.0" 200 9795 +190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.24.202.82 - - [25/Feb/2008:09:25:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:09:29:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.113.44.27 - - [25/Feb/2008:09:30:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:09:30:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.113.44.27 - - [25/Feb/2008:09:30:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +150.210.155.167 - - [25/Feb/2008:09:30:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +91.113.44.27 - - [25/Feb/2008:09:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +130.206.100.135 - - [25/Feb/2008:09:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +91.113.44.27 - - [25/Feb/2008:09:31:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +132.207.44.190 - - [25/Feb/2008:09:32:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.178.179.132 - - [25/Feb/2008:09:33:00 -0600] "GET /python.html HTTP/1.1" 200 18870 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.178.179.132 - - [25/Feb/2008:09:33:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:09:35:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:09:35:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.237.142.6 - - [25/Feb/2008:09:35:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:09:35:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +67.195.44.107 - - [25/Feb/2008:09:37:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [25/Feb/2008:09:37:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.221.197.20 - - [25/Feb/2008:09:38:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +82.245.141.168 - - [25/Feb/2008:09:39:06 -0600] "GET /cv.html HTTP/1.1" 200 31798 +209.85.136.136 - - [25/Feb/2008:09:39:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +194.237.142.6 - - [25/Feb/2008:09:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.85.136.136 - - [25/Feb/2008:09:39:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +194.237.142.6 - - [25/Feb/2008:09:39:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +81.48.212.152 - - [25/Feb/2008:09:39:34 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.26.119 - - [25/Feb/2008:09:39:43 -0600] "GET /ply/README HTTP/1.0" 200 8605 +132.207.44.190 - - [25/Feb/2008:09:42:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 +83.103.98.38 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:09:43:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +137.226.57.203 - - [25/Feb/2008:09:43:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +194.237.142.6 - - [25/Feb/2008:09:43:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +137.226.57.203 - - [25/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +137.226.57.203 - - [25/Feb/2008:09:43:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +81.48.212.152 - - [25/Feb/2008:09:45:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.48.212.152 - - [25/Feb/2008:09:46:03 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173064 +81.48.212.152 - - [25/Feb/2008:09:46:08 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173271 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET / HTTP/1.1" 200 4447 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.181.110 - - [25/Feb/2008:09:48:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.114.62.34 - - [25/Feb/2008:09:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:51:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.48.71.175 - - [25/Feb/2008:09:54:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:09:54:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.20.132.177 - - [25/Feb/2008:09:54:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.82 - - [25/Feb/2008:09:56:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE077.HTM HTTP/1.0" 304 - +134.160.173.1 - - [25/Feb/2008:09:57:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.160.173.1 - - [25/Feb/2008:09:57:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:57:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:58:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +137.226.57.203 - - [25/Feb/2008:09:58:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +137.226.57.203 - - [25/Feb/2008:09:58:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:09:58:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +134.160.173.1 - - [25/Feb/2008:09:59:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:10:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:10:07:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +91.113.44.27 - - [25/Feb/2008:10:07:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.113.44.27 - - [25/Feb/2008:10:07:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +91.113.44.27 - - [25/Feb/2008:10:07:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +91.113.44.27 - - [25/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCallbacks HTTP/1.1" 200 3411 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:10:08:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +91.113.44.27 - - [25/Feb/2008:10:08:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +84.20.132.177 - - [25/Feb/2008:10:11:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:10:11:21 -0600] "HEAD /video/MVI_1516.AVI HTTP/1.1" 404 0 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.218.61 - - [25/Feb/2008:10:12:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.26.231.162 - - [25/Feb/2008:10:13:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:15:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.26.231.162 - - [25/Feb/2008:10:17:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +131.44.121.252 - - [25/Feb/2008:10:18:30 -0600] "GET /python.html HTTP/1.1" 200 18870 +131.44.121.252 - - [25/Feb/2008:10:18:31 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply HTTP/1.1" 301 242 +213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.237.142.6 - - [25/Feb/2008:10:29:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.237.142.6 - - [25/Feb/2008:10:29:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.221.197.20 - - [25/Feb/2008:10:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +194.237.142.6 - - [25/Feb/2008:10:29:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.63.152.218 - - [25/Feb/2008:10:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.63.152.218 - - [25/Feb/2008:10:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +71.63.152.218 - - [25/Feb/2008:10:30:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +128.135.11.245 - - [25/Feb/2008:10:32:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.247.172.8 - - [25/Feb/2008:10:33:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.30.226.134 - - [25/Feb/2008:10:36:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.22.104.18 - - [25/Feb/2008:10:38:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [25/Feb/2008:10:41:08 -0600] "GET /ply/ HTTP/1.0" 304 - +82.166.58.226 - - [25/Feb/2008:10:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [25/Feb/2008:10:49:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +24.7.210.64 - - [25/Feb/2008:10:54:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [25/Feb/2008:10:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:10:55:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +196.25.255.246 - - [25/Feb/2008:10:56:18 -0600] "GET /ply/ HTTP/1.1" 304 - +82.239.61.147 - - [25/Feb/2008:10:59:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:10:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.7.210.64 - - [25/Feb/2008:11:01:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [25/Feb/2008:11:01:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:06:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.128.56.113 - - [25/Feb/2008:11:07:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +190.128.56.113 - - [25/Feb/2008:11:07:53 -0600] "GET /ply/index.html HTTP/1.1" 304 - +190.128.56.113 - - [25/Feb/2008:11:07:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.128.56.113 - - [25/Feb/2008:11:08:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.7.113 - - [25/Feb/2008:11:15:25 -0600] "GET /photos/wind/pages/IMG_1298.htm HTTP/1.0" 404 133 +38.104.0.30 - - [25/Feb/2008:11:27:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [25/Feb/2008:11:27:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +169.137.105.115 - - [25/Feb/2008:11:29:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +169.137.105.115 - - [25/Feb/2008:11:29:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /ply/ HTTP/1.1" 304 - +212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.240.156.251 - - [25/Feb/2008:11:31:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:11:33:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.67.152.241 - - [25/Feb/2008:11:41:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.67.152.241 - - [25/Feb/2008:11:41:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.67.152.241 - - [25/Feb/2008:11:41:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.151.244.16 - - [25/Feb/2008:11:43:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +208.22.104.18 - - [25/Feb/2008:11:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +80.229.34.140 - - [25/Feb/2008:11:50:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.229.34.140 - - [25/Feb/2008:11:50:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:02:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.164.194 - - [25/Feb/2008:12:03:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +170.252.64.1 - - [25/Feb/2008:12:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +170.252.64.1 - - [25/Feb/2008:12:04:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +128.135.164.194 - - [25/Feb/2008:12:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:05:01 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +170.252.64.1 - - [25/Feb/2008:12:06:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +170.252.64.1 - - [25/Feb/2008:12:06:27 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +170.252.64.1 - - [25/Feb/2008:12:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +170.252.64.1 - - [25/Feb/2008:12:06:41 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +128.135.11.245 - - [25/Feb/2008:12:07:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:12:08:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.30.84.93 - - [25/Feb/2008:12:15:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.72.97.51 - - [25/Feb/2008:12:35:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.72.97.51 - - [25/Feb/2008:12:36:27 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +68.72.97.51 - - [25/Feb/2008:12:36:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +70.137.112.2 - - [25/Feb/2008:12:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.194.136.18 - - [25/Feb/2008:12:37:19 -0600] "GET /ply HTTP/1.1" 301 242 +128.135.139.146 - - [25/Feb/2008:12:40:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.139.146 - - [25/Feb/2008:12:41:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +64.46.248.1 - - [25/Feb/2008:12:43:00 -0600] "GET / HTTP/1.0" 200 4447 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /python.html HTTP/1.0" 200 18870 +64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +64.46.248.1 - - [25/Feb/2008:12:43:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.81.80.193 - - [25/Feb/2008:12:46:39 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 186488 +71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 +71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:12:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:12:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [25/Feb/2008:12:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [25/Feb/2008:13:01:04 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.170.119.34 - - [25/Feb/2008:13:01:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.170.119.34 - - [25/Feb/2008:13:03:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.234.185.130 - - [25/Feb/2008:13:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.234.185.130 - - [25/Feb/2008:13:04:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [25/Feb/2008:13:05:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.23.48 - - [25/Feb/2008:13:05:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1331 +209.234.185.130 - - [25/Feb/2008:13:05:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +83.23.227.219 - - [25/Feb/2008:13:06:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.23.227.219 - - [25/Feb/2008:13:06:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.75.15.14 - - [25/Feb/2008:13:06:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1400 +206.75.15.14 - - [25/Feb/2008:13:06:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1026 +128.221.197.20 - - [25/Feb/2008:13:06:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +208.163.53.37 - - [25/Feb/2008:13:09:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.122.155.30 - - [25/Feb/2008:13:11:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.122.155.30 - - [25/Feb/2008:13:12:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +200.122.155.30 - - [25/Feb/2008:13:12:12 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +208.163.53.37 - - [25/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.163.53.37 - - [25/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.163.53.37 - - [25/Feb/2008:13:17:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.188 - - [25/Feb/2008:13:22:37 -0600] "GET /training.html HTTP/1.0" 304 - +82.73.225.225 - - [25/Feb/2008:13:22:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.73.225.225 - - [25/Feb/2008:13:22:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.73.225.225 - - [25/Feb/2008:13:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.73.225.225 - - [25/Feb/2008:13:22:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.195.58.186 - - [25/Feb/2008:13:23:06 -0600] "GET /about.html HTTP/1.0" 304 - +202.160.174.4 - - [25/Feb/2008:13:23:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +202.160.174.4 - - [25/Feb/2008:13:23:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [25/Feb/2008:13:23:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +67.195.58.165 - - [25/Feb/2008:13:23:18 -0600] "GET /writing.html HTTP/1.0" 304 - +81.52.143.16 - - [25/Feb/2008:13:25:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +82.239.61.147 - - [25/Feb/2008:13:25:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.52.143.16 - - [25/Feb/2008:13:25:58 -0600] "GET / HTTP/1.1" 200 4447 +82.239.61.147 - - [25/Feb/2008:13:26:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:13:26:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.77 - - [25/Feb/2008:13:29:39 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1510 +82.239.61.147 - - [25/Feb/2008:13:30:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:13:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +41.222.70.189 - - [25/Feb/2008:13:36:05 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 21384 +41.222.70.189 - - [25/Feb/2008:13:36:55 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 206 3108482 +200.122.155.30 - - [25/Feb/2008:13:41:03 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +200.122.155.30 - - [25/Feb/2008:13:41:11 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +71.57.91.136 - - [25/Feb/2008:13:43:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.194 - - [25/Feb/2008:13:48:38 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +219.93.175.69 - - [25/Feb/2008:13:48:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +200.13.243.76 - - [25/Feb/2008:13:48:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +190.30.84.93 - - [25/Feb/2008:13:52:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.221.197.20 - - [25/Feb/2008:13:52:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +62.121.64.81 - - [25/Feb/2008:13:52:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:53:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:53:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +62.121.64.81 - - [25/Feb/2008:13:53:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +62.121.64.81 - - [25/Feb/2008:13:53:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 +62.121.64.81 - - [25/Feb/2008:13:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +85.194.45.170 - - [25/Feb/2008:14:04:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [25/Feb/2008:14:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.31.165 - - [25/Feb/2008:14:05:19 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [25/Feb/2008:14:05:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 +85.194.45.170 - - [25/Feb/2008:14:07:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.194.45.170 - - [25/Feb/2008:14:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.192.69.3 - - [25/Feb/2008:14:09:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +206.192.69.3 - - [25/Feb/2008:14:10:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.150 - - [25/Feb/2008:14:11:32 -0600] "GET /publications.html HTTP/1.0" 200 7758 +204.154.183.65 - - [25/Feb/2008:14:11:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.171 - - [25/Feb/2008:14:11:57 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE082.HTM HTTP/1.0" 200 1526 +199.171.86.151 - - [25/Feb/2008:14:12:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +199.171.86.151 - - [25/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +199.171.86.151 - - [25/Feb/2008:14:12:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +199.171.86.151 - - [25/Feb/2008:14:13:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +199.171.86.151 - - [25/Feb/2008:14:13:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +74.6.31.151 - - [25/Feb/2008:14:14:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +204.154.183.65 - - [25/Feb/2008:14:15:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.248.57.218 - - [25/Feb/2008:14:18:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.248.57.218 - - [25/Feb/2008:14:18:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.138.87.130 - - [25/Feb/2008:14:20:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.80.193.52 - - [25/Feb/2008:14:26:08 -0600] "GET /cgi-bin/wiki.pl?action=browse&diff=1&id=swigfaq/sharedlibraries HTTP/1.1" 200 1528 +85.194.45.170 - - [25/Feb/2008:14:27:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:28:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:29:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.132.99.224 - - [25/Feb/2008:14:29:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.132.99.224 - - [25/Feb/2008:14:29:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +80.132.99.224 - - [25/Feb/2008:14:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.132.99.224 - - [25/Feb/2008:14:29:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.132.99.224 - - [25/Feb/2008:14:29:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +208.22.104.18 - - [25/Feb/2008:14:31:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.22.104.18 - - [25/Feb/2008:14:31:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:14:32:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +208.22.104.18 - - [25/Feb/2008:14:32:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +88.114.145.139 - - [25/Feb/2008:14:34:38 -0600] "GET /python.html HTTP/1.1" 200 18870 +88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12634 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.224.58.211 - - [25/Feb/2008:14:38:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.69.221.130 - - [25/Feb/2008:14:38:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +128.135.194.138 - - [25/Feb/2008:14:45:12 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +128.135.194.138 - - [25/Feb/2008:14:45:19 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +81.52.143.15 - - [25/Feb/2008:14:45:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 +81.52.143.15 - - [25/Feb/2008:14:45:47 -0600] "GET /ply HTTP/1.1" 301 242 +128.135.194.138 - - [25/Feb/2008:14:45:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.110.251.215 - - [25/Feb/2008:14:45:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.241.68.226 - - [25/Feb/2008:14:49:22 -0600] "GET / HTTP/1.1" 200 4447 +66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.241.68.226 - - [25/Feb/2008:14:50:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +66.241.68.226 - - [25/Feb/2008:14:50:08 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +66.241.68.226 - - [25/Feb/2008:14:51:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 +66.241.68.226 - - [25/Feb/2008:14:51:47 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +66.241.68.226 - - [25/Feb/2008:14:52:14 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +66.241.68.226 - - [25/Feb/2008:14:54:41 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +66.241.68.226 - - [25/Feb/2008:14:54:44 -0600] "GET /diet.html HTTP/1.1" 404 133 +200.171.34.14 - - [25/Feb/2008:14:55:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +200.171.34.14 - - [25/Feb/2008:14:55:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.216 - - [25/Feb/2008:14:55:46 -0600] "GET / HTTP/1.0" 304 - +80.132.99.224 - - [25/Feb/2008:14:56:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +128.221.197.20 - - [25/Feb/2008:15:01:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +70.137.112.2 - - [25/Feb/2008:15:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.34.14 - - [25/Feb/2008:15:04:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +200.171.34.14 - - [25/Feb/2008:15:04:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:15:09:02 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.106.32.126 - - [25/Feb/2008:15:10:41 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1827 +129.106.32.126 - - [25/Feb/2008:15:10:44 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 +129.106.32.126 - - [25/Feb/2008:15:10:58 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +129.106.32.126 - - [25/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +129.106.32.126 - - [25/Feb/2008:15:11:04 -0600] "GET /cgi-bin/wiki.pl?InlineDirective HTTP/1.1" 200 2103 +129.106.32.126 - - [25/Feb/2008:15:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +129.106.32.126 - - [25/Feb/2008:15:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +129.106.32.126 - - [25/Feb/2008:15:11:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +129.106.32.126 - - [25/Feb/2008:15:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +129.106.32.126 - - [25/Feb/2008:15:11:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +129.106.32.126 - - [25/Feb/2008:15:11:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.83.72.201 - - [25/Feb/2008:15:14:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +160.83.72.201 - - [25/Feb/2008:15:14:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +160.83.72.201 - - [25/Feb/2008:15:14:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 +89.142.106.15 - - [25/Feb/2008:15:16:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +89.142.106.15 - - [25/Feb/2008:15:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.142.106.15 - - [25/Feb/2008:15:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +89.142.106.15 - - [25/Feb/2008:15:16:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +89.142.106.15 - - [25/Feb/2008:15:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +160.83.72.201 - - [25/Feb/2008:15:17:13 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 1167 +74.6.19.115 - - [25/Feb/2008:15:17:56 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +62.224.58.211 - - [25/Feb/2008:15:18:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:15:18:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:20:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +160.83.72.201 - - [25/Feb/2008:15:21:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +160.83.72.201 - - [25/Feb/2008:15:21:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +74.6.26.100 - - [25/Feb/2008:15:22:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.0" 200 2184 +160.83.72.201 - - [25/Feb/2008:15:22:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +208.22.104.18 - - [25/Feb/2008:15:24:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +86.205.155.128 - - [25/Feb/2008:15:24:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.205.155.128 - - [25/Feb/2008:15:24:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:15:25:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:15:39:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.239.61.147 - - [25/Feb/2008:15:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.239.61.147 - - [25/Feb/2008:15:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:15:39:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.35.206.246 - - [25/Feb/2008:15:50:02 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +82.35.206.246 - - [25/Feb/2008:15:50:09 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +82.35.206.246 - - [25/Feb/2008:15:50:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +82.35.206.246 - - [25/Feb/2008:15:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.35.206.246 - - [25/Feb/2008:15:50:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +82.35.206.246 - - [25/Feb/2008:15:51:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.35.206.246 - - [25/Feb/2008:15:55:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.35.206.246 - - [25/Feb/2008:15:55:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +203.166.87.218 - - [25/Feb/2008:15:58:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +203.166.87.218 - - [25/Feb/2008:15:58:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +203.166.87.218 - - [25/Feb/2008:15:59:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=SwigFaqDLLForWindows/Msys HTTP/1.1" 200 1927 +203.166.87.218 - - [25/Feb/2008:15:59:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic HTTP/1.1" 301 246 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.247.8 - - [25/Feb/2008:16:03:09 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +68.252.247.8 - - [25/Feb/2008:16:03:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:16:09:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +76.223.13.234 - - [25/Feb/2008:16:09:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +88.191.19.81 - - [25/Feb/2008:16:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.157.119.197 - - [25/Feb/2008:16:11:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.134.210 - - [25/Feb/2008:16:23:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.204.240.53 - - [25/Feb/2008:16:23:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.204.240.53 - - [25/Feb/2008:16:23:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.91.134.210 - - [25/Feb/2008:16:23:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +140.160.129.28 - - [25/Feb/2008:16:25:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.28.166 - - [25/Feb/2008:16:27:33 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1346 +69.91.134.210 - - [25/Feb/2008:16:31:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.113.40.1 - - [25/Feb/2008:16:31:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET / HTTP/1.1" 200 4447 +128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.24.9 - - [25/Feb/2008:16:36:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.24.9 - - [25/Feb/2008:16:36:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.74.95.23 - - [25/Feb/2008:16:36:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [25/Feb/2008:16:37:16 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.135.24.9 - - [25/Feb/2008:16:37:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +87.74.95.23 - - [25/Feb/2008:16:38:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [25/Feb/2008:16:40:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +140.160.199.105 - - [25/Feb/2008:16:48:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.160.199.105 - - [25/Feb/2008:16:49:10 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +87.74.95.23 - - [25/Feb/2008:16:51:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.74.95.23 - - [25/Feb/2008:16:51:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET / HTTP/1.0" 304 - +200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.21.98.7 - - [25/Feb/2008:16:58:42 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +200.21.98.7 - - [25/Feb/2008:16:58:56 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 494 +210.143.35.13 - - [25/Feb/2008:16:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.21.98.7 - - [25/Feb/2008:16:59:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 133088 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 +204.246.129.196 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.253.252.20 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.189.175 - - [25/Feb/2008:17:03:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.175 - - [25/Feb/2008:17:03:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1002 +200.21.98.7 - - [25/Feb/2008:17:05:37 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 43106 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET / HTTP/1.1" 200 4447 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.191.75 - - [25/Feb/2008:17:10:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.191.75 - - [25/Feb/2008:17:10:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 +81.48.212.152 - - [25/Feb/2008:17:11:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +190.139.116.130 - - [25/Feb/2008:17:15:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 +76.213.231.36 - - [25/Feb/2008:17:16:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.213.231.36 - - [25/Feb/2008:17:16:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +210.143.35.13 - - [25/Feb/2008:17:18:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.13 - - [25/Feb/2008:17:18:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.172.156.57 - - [25/Feb/2008:17:23:23 -0600] "GET /python.html HTTP/1.1" 200 18870 +58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.187.74 - - [25/Feb/2008:17:24:54 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [25/Feb/2008:17:24:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +210.143.35.13 - - [25/Feb/2008:17:25:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20232 +88.191.19.81 - - [25/Feb/2008:17:27:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.49.99.11 - - [25/Feb/2008:17:30:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.49.99.11 - - [25/Feb/2008:17:30:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +41.221.16.105 - - [25/Feb/2008:17:34:09 -0600] "GET / HTTP/1.1" 200 4447 +41.221.16.105 - - [25/Feb/2008:17:34:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET / HTTP/1.1" 200 4447 +220.194.55.45 - - [25/Feb/2008:17:42:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.135.24.9 - - [25/Feb/2008:17:48:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +75.58.86.1 - - [25/Feb/2008:17:48:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:17:50:45 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:17:50:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:17:50:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:17:51:19 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.143.231.202 - - [25/Feb/2008:17:53:51 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.154 - - [25/Feb/2008:18:00:43 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.0" 200 3246437 +85.160.19.27 - - [25/Feb/2008:18:02:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +85.160.19.27 - - [25/Feb/2008:18:02:47 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:18:03:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.17.146.129 - - [25/Feb/2008:18:03:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +209.17.146.129 - - [25/Feb/2008:18:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:18:03:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:04:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:04:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1381 +216.15.126.192 - - [25/Feb/2008:18:04:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 +121.139.76.33 - - [25/Feb/2008:18:09:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +121.139.76.33 - - [25/Feb/2008:18:09:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 +121.139.76.33 - - [25/Feb/2008:18:09:58 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 +75.58.86.1 - - [25/Feb/2008:18:13:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - +75.58.86.1 - - [25/Feb/2008:18:13:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.195.58.174 - - [25/Feb/2008:18:14:23 -0600] "GET /ply/ HTTP/1.0" 304 - +128.135.24.238 - - [25/Feb/2008:18:14:25 -0600] "GET /dynamic/ffcache.zip HTTP/1.0" 200 4919642 +67.195.58.170 - - [25/Feb/2008:18:14:58 -0600] "GET / HTTP/1.0" 200 4447 +67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/README HTTP/1.0" 200 8605 +67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 +67.195.58.158 - - [25/Feb/2008:18:15:33 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +85.160.19.27 - - [25/Feb/2008:18:15:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:18:21:58 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +74.6.31.145 - - [25/Feb/2008:18:23:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.144.143.13 - - [25/Feb/2008:18:29:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.134.126 - - [25/Feb/2008:18:46:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.134.126 - - [25/Feb/2008:18:46:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +61.135.190.17 - - [25/Feb/2008:18:51:13 -0600] "GET /ply/ HTTP/1.1" 304 - +85.160.19.27 - - [25/Feb/2008:18:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:18:58:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.169 - - [25/Feb/2008:18:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:18:59:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:19:03:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:19:04:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1265 +130.240.203.74 - - [25/Feb/2008:19:10:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.231.169 - - [25/Feb/2008:19:10:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.240.203.74 - - [25/Feb/2008:19:10:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +70.245.137.240 - - [25/Feb/2008:19:15:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.137.240 - - [25/Feb/2008:19:15:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.137.240 - - [25/Feb/2008:19:15:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:17:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +24.249.206.181 - - [25/Feb/2008:19:17:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +24.249.206.181 - - [25/Feb/2008:19:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +128.143.231.169 - - [25/Feb/2008:19:18:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:18:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +24.249.206.181 - - [25/Feb/2008:19:19:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +24.249.206.181 - - [25/Feb/2008:19:20:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +202.179.180.52 - - [25/Feb/2008:19:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [25/Feb/2008:19:20:30 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 +128.143.231.169 - - [25/Feb/2008:19:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply HTTP/1.1" 301 242 +82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [25/Feb/2008:19:32:11 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.99.169.3 - - [25/Feb/2008:19:32:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.99.169.3 - - [25/Feb/2008:19:32:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 +74.6.26.22 - - [25/Feb/2008:19:32:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 +98.224.230.125 - - [25/Feb/2008:19:34:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +98.224.230.125 - - [25/Feb/2008:19:34:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.224.230.125 - - [25/Feb/2008:19:35:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +98.224.230.125 - - [25/Feb/2008:19:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +98.224.230.125 - - [25/Feb/2008:19:36:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +98.224.230.125 - - [25/Feb/2008:19:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +74.6.19.237 - - [25/Feb/2008:19:38:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 +74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +71.3.94.70 - - [25/Feb/2008:19:45:21 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +69.113.211.8 - - [25/Feb/2008:19:50:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 +69.113.211.8 - - [25/Feb/2008:19:50:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +128.143.231.169 - - [25/Feb/2008:19:51:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [25/Feb/2008:19:54:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.148.212.222 - - [25/Feb/2008:19:54:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [25/Feb/2008:19:54:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.148.212.222 - - [25/Feb/2008:19:54:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +216.148.212.222 - - [25/Feb/2008:19:58:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.148.212.222 - - [25/Feb/2008:19:58:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +129.82.46.120 - - [25/Feb/2008:20:02:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.186.98.20 - - [25/Feb/2008:20:06:04 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 628284 +67.186.98.20 - - [25/Feb/2008:20:06:12 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 2935451 +67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [25/Feb/2008:20:07:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.186.98.20 - - [25/Feb/2008:20:12:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 199044 +67.186.98.20 - - [25/Feb/2008:20:12:28 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 +216.148.212.222 - - [25/Feb/2008:20:12:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +148.241.82.6 - - [25/Feb/2008:20:12:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.82.6 - - [25/Feb/2008:20:12:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.82.6 - - [25/Feb/2008:20:12:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.82.6 - - [25/Feb/2008:20:13:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +129.82.46.120 - - [25/Feb/2008:20:13:22 -0600] "GET /ply/ HTTP/1.1" 304 - +129.82.46.120 - - [25/Feb/2008:20:13:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +193.202.115.177 - - [25/Feb/2008:20:16:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.8.73 - - [25/Feb/2008:20:20:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +71.57.91.136 - - [25/Feb/2008:20:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.36.158.68 - - [25/Feb/2008:20:20:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +69.36.158.68 - - [25/Feb/2008:20:20:35 -0600] "GET / HTTP/1.1" 200 4447 +69.36.158.68 - - [25/Feb/2008:20:20:36 -0600] "GET /index.html HTTP/1.1" 200 4447 +69.36.158.68 - - [25/Feb/2008:20:20:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +69.36.158.68 - - [25/Feb/2008:20:20:38 -0600] "GET /software.html HTTP/1.1" 200 3163 +69.36.158.68 - - [25/Feb/2008:20:20:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +69.36.158.68 - - [25/Feb/2008:20:20:40 -0600] "GET /writing.html HTTP/1.1" 200 2871 +69.36.158.68 - - [25/Feb/2008:20:20:41 -0600] "GET /about.html HTTP/1.1" 200 7890 +69.36.158.68 - - [25/Feb/2008:20:20:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.36.158.68 - - [25/Feb/2008:20:20:44 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +69.36.158.68 - - [25/Feb/2008:20:20:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +69.36.158.68 - - [25/Feb/2008:20:20:46 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +69.36.158.68 - - [25/Feb/2008:20:20:47 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +69.36.158.68 - - [25/Feb/2008:20:20:48 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +69.36.158.68 - - [25/Feb/2008:20:20:49 -0600] "GET /publications.html HTTP/1.1" 200 7758 +69.36.158.68 - - [25/Feb/2008:20:20:50 -0600] "GET /cv.html HTTP/1.1" 200 31798 +69.36.158.68 - - [25/Feb/2008:20:20:51 -0600] "GET /diet.html HTTP/1.1" 404 133 +69.36.158.68 - - [25/Feb/2008:20:20:52 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +69.36.158.68 - - [25/Feb/2008:20:20:53 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +69.36.158.68 - - [25/Feb/2008:20:20:54 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +69.36.158.68 - - [25/Feb/2008:20:20:55 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +69.36.158.68 - - [25/Feb/2008:20:20:56 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/ HTTP/1.1" 304 - +148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +201.213.27.208 - - [25/Feb/2008:20:22:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.213.27.208 - - [25/Feb/2008:20:22:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.213.27.208 - - [25/Feb/2008:20:22:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.213.27.208 - - [25/Feb/2008:20:22:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.186.98.20 - - [25/Feb/2008:20:28:07 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 250956 +67.186.98.20 - - [25/Feb/2008:20:28:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 +71.57.91.136 - - [25/Feb/2008:20:35:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.87.213.22 - - [25/Feb/2008:20:37:26 -0600] "GET / HTTP/1.1" 200 4447 +12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +129.82.46.120 - - [25/Feb/2008:20:38:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +12.87.213.22 - - [25/Feb/2008:20:39:06 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +217.172.44.82 - - [25/Feb/2008:20:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.82.46.120 - - [25/Feb/2008:20:53:39 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:20:55:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +72.85.134.143 - - [25/Feb/2008:20:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.160 - - [25/Feb/2008:21:03:47 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE045.HTM HTTP/1.0" 304 - +217.196.43.134 - - [25/Feb/2008:21:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.47.57.29 - - [25/Feb/2008:21:09:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.47.57.29 - - [25/Feb/2008:21:09:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.47.57.29 - - [25/Feb/2008:21:09:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:21:09:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +75.22.21.146 - - [25/Feb/2008:21:09:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:21:09:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +75.22.21.146 - - [25/Feb/2008:21:09:59 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.1" 304 - +71.57.91.136 - - [25/Feb/2008:21:19:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:21:19:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.149 - - [25/Feb/2008:21:23:01 -0600] "GET /about.html HTTP/1.0" 200 7890 +75.22.21.146 - - [25/Feb/2008:21:25:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +64.124.85.75 - - [25/Feb/2008:21:36:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.75 - - [25/Feb/2008:21:39:27 -0600] "GET /ply HTTP/1.1" 301 242 +129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +129.82.47.96 - - [25/Feb/2008:21:41:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.82.47.96 - - [25/Feb/2008:21:41:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.232.164.26 - - [25/Feb/2008:21:49:43 -0600] "GET /about.html HTTP/1.0" 200 7890 +38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [25/Feb/2008:22:05:01 -0600] "GET / HTTP/1.1" 200 4447 +202.118.7.10 - - [25/Feb/2008:22:08:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +202.118.7.10 - - [25/Feb/2008:22:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.118.7.10 - - [25/Feb/2008:22:08:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.118.7.10 - - [25/Feb/2008:22:09:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.191.64.124 - - [25/Feb/2008:22:15:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.104.0.30 - - [25/Feb/2008:22:15:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [25/Feb/2008:22:15:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +202.118.7.10 - - [25/Feb/2008:22:17:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.72.123.219 - - [25/Feb/2008:22:18:25 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +74.6.28.76 - - [25/Feb/2008:22:21:28 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1409 +203.200.218.2 - - [25/Feb/2008:22:24:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.200.218.2 - - [25/Feb/2008:22:24:15 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.200.218.2 - - [25/Feb/2008:22:24:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.135.15.12 - - [25/Feb/2008:22:24:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.72 - - [25/Feb/2008:22:25:31 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +203.200.218.2 - - [25/Feb/2008:22:26:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.236.226.90 - - [25/Feb/2008:22:26:38 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 15733 +201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:22:26:52 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +129.82.47.96 - - [25/Feb/2008:22:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic HTTP/1.1" 301 246 +66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +66.146.214.212 - - [25/Feb/2008:22:31:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +129.82.47.96 - - [25/Feb/2008:22:34:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.24 - - [25/Feb/2008:22:36:03 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +66.116.72.114 - - [25/Feb/2008:22:37:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [25/Feb/2008:22:37:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:40:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.94.167.76 - - [25/Feb/2008:22:41:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:41:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +203.94.167.76 - - [25/Feb/2008:22:41:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +76.199.14.198 - - [25/Feb/2008:22:42:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +66.146.214.212 - - [25/Feb/2008:22:43:06 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +84.110.229.145 - - [25/Feb/2008:22:45:32 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.229.145 - - [25/Feb/2008:22:45:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +202.179.180.45 - - [25/Feb/2008:22:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.45 - - [25/Feb/2008:22:49:11 -0600] "GET / HTTP/1.1" 200 4447 +202.179.180.45 - - [25/Feb/2008:22:49:18 -0600] "GET /index.html HTTP/1.1" 200 4447 +202.179.180.45 - - [25/Feb/2008:22:49:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +202.179.180.45 - - [25/Feb/2008:22:49:30 -0600] "GET /software.html HTTP/1.1" 200 3163 +202.179.180.45 - - [25/Feb/2008:22:49:37 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +202.179.180.45 - - [25/Feb/2008:22:49:43 -0600] "GET /writing.html HTTP/1.1" 200 2871 +202.179.180.45 - - [25/Feb/2008:22:49:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +202.179.180.45 - - [25/Feb/2008:22:49:56 -0600] "GET /python.html HTTP/1.1" 200 18870 +202.179.180.45 - - [25/Feb/2008:22:50:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +202.179.180.45 - - [25/Feb/2008:22:50:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +202.179.180.45 - - [25/Feb/2008:22:50:15 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +202.179.180.45 - - [25/Feb/2008:22:50:21 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +202.179.180.45 - - [25/Feb/2008:22:50:28 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +202.179.180.45 - - [25/Feb/2008:22:50:32 -0600] "GET /publications.html HTTP/1.1" 200 7758 +202.179.180.45 - - [25/Feb/2008:22:50:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 +68.226.202.243 - - [25/Feb/2008:22:50:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.226.202.243 - - [25/Feb/2008:22:51:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +68.226.202.243 - - [25/Feb/2008:22:51:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +58.71.35.197 - - [25/Feb/2008:22:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.71.35.197 - - [25/Feb/2008:22:58:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.71.35.197 - - [25/Feb/2008:22:58:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:22:59:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.182.43.140 - - [25/Feb/2008:23:11:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.110 - - [25/Feb/2008:23:11:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.20.44 - - [25/Feb/2008:23:15:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 +61.247.217.37 - - [25/Feb/2008:23:16:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +58.71.35.197 - - [25/Feb/2008:23:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.67.135.113 - - [25/Feb/2008:23:24:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +61.67.135.113 - - [25/Feb/2008:23:24:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +24.1.247.118 - - [25/Feb/2008:23:26:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [25/Feb/2008:23:26:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +58.71.35.197 - - [25/Feb/2008:23:27:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:27:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:28:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:23:28:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE043.HTM HTTP/1.1" 200 1178 +74.125.16.5 - - [25/Feb/2008:23:30:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +63.252.164.2 - - [25/Feb/2008:23:30:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.30.188.110 - - [25/Feb/2008:23:32:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.118 - - [25/Feb/2008:23:37:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [25/Feb/2008:23:37:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +124.30.188.110 - - [25/Feb/2008:23:39:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.8.73 - - [25/Feb/2008:23:42:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.8.73 - - [25/Feb/2008:23:42:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +65.214.45.129 - - [25/Feb/2008:23:43:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 +58.71.35.197 - - [25/Feb/2008:23:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.36.114.239 - - [25/Feb/2008:23:55:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.208.118 - - [25/Feb/2008:23:55:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 +58.71.35.197 - - [25/Feb/2008:23:57:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.115.4.222 - - [25/Feb/2008:23:57:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +74.6.26.171 - - [26/Feb/2008:00:00:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.25.81 - - [26/Feb/2008:00:00:44 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +66.249.65.37 - - [26/Feb/2008:00:02:13 -0600] "GET /training.html HTTP/1.1" 304 - +58.71.35.197 - - [26/Feb/2008:00:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [26/Feb/2008:00:04:45 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 +75.50.51.97 - - [26/Feb/2008:00:07:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.249.65.37 - - [26/Feb/2008:00:08:43 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - +213.145.165.82 - - [26/Feb/2008:00:16:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.8.73 - - [26/Feb/2008:00:17:23 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - +66.249.65.37 - - [26/Feb/2008:00:23:16 -0600] "GET /ply/example.html HTTP/1.1" 304 - +220.134.105.172 - - [26/Feb/2008:00:23:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.134.105.172 - - [26/Feb/2008:00:25:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.8.211.11 - - [26/Feb/2008:00:25:28 -0600] "GET /python.html HTTP/1.0" 200 18870 +220.134.105.172 - - [26/Feb/2008:00:25:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.8.211.11 - - [26/Feb/2008:00:25:59 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.134.105.172 - - [26/Feb/2008:00:26:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +84.110.232.251 - - [26/Feb/2008:00:34:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.232.251 - - [26/Feb/2008:00:34:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1017 +220.134.105.172 - - [26/Feb/2008:00:39:04 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.165.73.111 - - [26/Feb/2008:00:58:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +207.225.146.142 - - [26/Feb/2008:00:58:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +207.225.146.142 - - [26/Feb/2008:00:58:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +89.165.73.111 - - [26/Feb/2008:00:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:59:17 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +89.165.73.111 - - [26/Feb/2008:01:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:01:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:03:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:04:18 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 +99.140.232.220 - - [26/Feb/2008:01:04:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [26/Feb/2008:01:05:03 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +66.249.65.37 - - [26/Feb/2008:01:06:05 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 304 - +74.6.8.73 - - [26/Feb/2008:01:06:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +74.6.8.73 - - [26/Feb/2008:01:13:57 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.91.239.214 - - [26/Feb/2008:01:19:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.141.176.22 - - [26/Feb/2008:01:24:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +84.143.76.58 - - [26/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.143.76.58 - - [26/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.115.4.219 - - [26/Feb/2008:01:30:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +66.249.65.37 - - [26/Feb/2008:01:33:15 -0600] "GET /ply/support.html HTTP/1.1" 304 - +85.185.76.213 - - [26/Feb/2008:01:33:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.185.76.213 - - [26/Feb/2008:01:33:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.185.76.213 - - [26/Feb/2008:01:38:51 -0600] "GET /ply/ HTTP/1.1" 304 - +85.185.76.213 - - [26/Feb/2008:01:38:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.216.4.6 - - [26/Feb/2008:01:47:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.216.4.6 - - [26/Feb/2008:01:47:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.216.4.6 - - [26/Feb/2008:01:47:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.216.4.6 - - [26/Feb/2008:01:47:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.216.4.6 - - [26/Feb/2008:01:48:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.216.4.6 - - [26/Feb/2008:01:48:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +71.216.4.6 - - [26/Feb/2008:01:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +82.98.238.29 - - [26/Feb/2008:02:02:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.98.238.29 - - [26/Feb/2008:02:02:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.98.238.29 - - [26/Feb/2008:02:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.98.238.29 - - [26/Feb/2008:02:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.170 - - [26/Feb/2008:02:03:41 -0600] "GET /ply/README HTTP/1.1" 200 8605 +84.143.76.58 - - [26/Feb/2008:02:11:02 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.78.34.166 - - [26/Feb/2008:02:11:25 -0600] "GET / HTTP/1.1" 200 4447 +74.6.25.20 - - [26/Feb/2008:02:14:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.174 - - [26/Feb/2008:02:14:32 -0600] "GET /ply/ HTTP/1.0" 304 - +88.191.19.81 - - [26/Feb/2008:02:16:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.78.34.170 - - [26/Feb/2008:02:16:47 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 24524 +213.194.211.50 - - [26/Feb/2008:02:17:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:02:17:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:17:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.174 - - [26/Feb/2008:02:21:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +202.81.69.153 - - [26/Feb/2008:02:21:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +202.81.69.153 - - [26/Feb/2008:02:22:07 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +72.51.43.159 - - [26/Feb/2008:02:23:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +67.78.34.166 - - [26/Feb/2008:02:27:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.111.46.17 - - [26/Feb/2008:02:27:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +193.111.46.17 - - [26/Feb/2008:02:27:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.111.46.17 - - [26/Feb/2008:02:27:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 +193.111.46.17 - - [26/Feb/2008:02:27:43 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.0" 200 5873 +213.194.211.50 - - [26/Feb/2008:02:29:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:30:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.78.34.166 - - [26/Feb/2008:02:32:07 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +67.78.34.170 - - [26/Feb/2008:02:36:25 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 12820 +74.6.23.210 - - [26/Feb/2008:02:37:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 +80.99.119.201 - - [26/Feb/2008:02:38:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +88.191.19.81 - - [26/Feb/2008:02:39:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +202.81.69.153 - - [26/Feb/2008:02:42:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +79.179.99.175 - - [26/Feb/2008:02:42:19 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +67.78.34.174 - - [26/Feb/2008:02:42:20 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 12819 +88.131.106.15 - - [26/Feb/2008:02:42:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 +88.131.106.15 - - [26/Feb/2008:02:42:58 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.214.45.129 - - [26/Feb/2008:02:42:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +221.6.82.112 - - [26/Feb/2008:02:43:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.6.82.112 - - [26/Feb/2008:02:43:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +206.51.226.87 - - [26/Feb/2008:02:43:42 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +87.101.244.6 - - [26/Feb/2008:02:43:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +67.207.145.238 - - [26/Feb/2008:02:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +221.6.82.112 - - [26/Feb/2008:02:44:39 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 15480 +221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +221.6.82.112 - - [26/Feb/2008:02:44:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 206 36955 +221.6.82.112 - - [26/Feb/2008:02:45:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.99.119.201 - - [26/Feb/2008:02:45:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.119.201 - - [26/Feb/2008:02:45:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.30.226.4 - - [26/Feb/2008:02:47:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.78.34.170 - - [26/Feb/2008:02:47:27 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 12819 +66.232.113.62 - - [26/Feb/2008:02:47:41 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +80.97.94.178 - - [26/Feb/2008:02:47:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.18.38.242 - - [26/Feb/2008:02:47:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +67.78.34.170 - - [26/Feb/2008:02:52:27 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.1" 200 12819 +83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.214.45.129 - - [26/Feb/2008:02:56:29 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 +67.78.34.174 - - [26/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 12819 +202.81.69.153 - - [26/Feb/2008:03:00:35 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 100406 +202.81.69.153 - - [26/Feb/2008:03:00:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +88.191.19.81 - - [26/Feb/2008:03:00:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.78.34.166 - - [26/Feb/2008:03:04:27 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 12819 +71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [26/Feb/2008:03:04:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [26/Feb/2008:03:05:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.166 - - [26/Feb/2008:03:11:34 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 12820 +67.78.34.166 - - [26/Feb/2008:03:17:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 12818 +67.78.34.166 - - [26/Feb/2008:03:21:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.1" 200 12820 +74.6.23.102 - - [26/Feb/2008:03:23:08 -0600] "GET /index.html HTTP/1.0" 304 - +67.78.34.170 - - [26/Feb/2008:03:24:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 12818 +74.6.8.73 - - [26/Feb/2008:03:25:05 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 +62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +67.78.34.174 - - [26/Feb/2008:03:29:51 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 12818 +137.226.57.203 - - [26/Feb/2008:03:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:03:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:03:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.134.105.172 - - [26/Feb/2008:03:31:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +218.94.9.35 - - [26/Feb/2008:03:33:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.94.9.35 - - [26/Feb/2008:03:33:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.94.9.35 - - [26/Feb/2008:03:33:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.94.9.35 - - [26/Feb/2008:03:33:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.8.73 - - [26/Feb/2008:03:39:26 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +67.78.34.170 - - [26/Feb/2008:03:43:01 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 12820 +203.123.162.235 - - [26/Feb/2008:03:44:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.123.162.235 - - [26/Feb/2008:03:44:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:58 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:03:45:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.166 - - [26/Feb/2008:03:53:00 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 200 12819 +75.125.165.10 - - [26/Feb/2008:03:57:36 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.119 - - [26/Feb/2008:03:58:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [26/Feb/2008:03:58:21 -0600] "GET /swill/Doc/index.html HTTP/1.1" 304 - +128.165.207.58 - - [26/Feb/2008:04:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [26/Feb/2008:04:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.167.100.246 - - [26/Feb/2008:04:05:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +99.167.100.246 - - [26/Feb/2008:04:05:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:04:05:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.100.246 - - [26/Feb/2008:04:05:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +202.81.69.153 - - [26/Feb/2008:04:06:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply HTTP/1.1" 301 242 +192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:04:25:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:33:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +160.45.115.176 - - [26/Feb/2008:04:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +61.57.149.13 - - [26/Feb/2008:04:35:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +61.57.149.13 - - [26/Feb/2008:04:35:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +195.217.226.30 - - [26/Feb/2008:04:44:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.217.226.30 - - [26/Feb/2008:04:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.217.226.30 - - [26/Feb/2008:04:44:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 935 +160.45.115.176 - - [26/Feb/2008:04:48:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:48:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +82.239.61.147 - - [26/Feb/2008:04:54:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [26/Feb/2008:04:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:04:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:56:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:56:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +59.93.91.251 - - [26/Feb/2008:05:00:41 -0600] "GET /python.html HTTP/1.0" 200 18870 +59.93.91.251 - - [26/Feb/2008:05:00:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +59.93.91.251 - - [26/Feb/2008:05:00:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:05:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.120.121.126 - - [26/Feb/2008:05:04:01 -0600] "GET /ply/ HTTP/1.0" 200 8018 +220.181.38.169 - - [26/Feb/2008:05:05:24 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET / HTTP/1.1" 200 4447 +160.45.115.176 - - [26/Feb/2008:05:08:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.93.91.251 - - [26/Feb/2008:05:09:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.25.29 - - [26/Feb/2008:05:09:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +61.135.166.102 - - [26/Feb/2008:05:10:12 -0600] "GET / HTTP/1.1" 200 4447 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:58 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +213.180.137.172 - - [26/Feb/2008:05:13:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +150.254.74.235 - - [26/Feb/2008:05:16:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 +150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +150.254.74.235 - - [26/Feb/2008:05:16:58 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.58.212.53 - - [26/Feb/2008:05:20:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:05:28:38 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.224.90 - - [26/Feb/2008:05:29:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.219.208.169 - - [26/Feb/2008:05:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.219.208.169 - - [26/Feb/2008:05:45:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.219.208.169 - - [26/Feb/2008:05:45:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +137.226.57.203 - - [26/Feb/2008:05:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:05:55:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:05:55:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.34.14 - - [26/Feb/2008:05:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +200.171.34.14 - - [26/Feb/2008:05:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.171.98.139 - - [26/Feb/2008:06:02:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +203.171.98.139 - - [26/Feb/2008:06:02:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 +217.218.215.8 - - [26/Feb/2008:06:03:32 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +217.218.215.8 - - [26/Feb/2008:06:04:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:04:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +62.219.208.169 - - [26/Feb/2008:06:05:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.149 - - [26/Feb/2008:06:05:49 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.0" 200 731143 +203.171.98.139 - - [26/Feb/2008:06:05:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +203.171.98.139 - - [26/Feb/2008:06:06:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +203.171.98.139 - - [26/Feb/2008:06:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +62.219.208.169 - - [26/Feb/2008:06:08:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:06:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:11:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +193.213.227.243 - - [26/Feb/2008:06:11:25 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:06:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +193.213.227.243 - - [26/Feb/2008:06:11:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +122.55.236.192 - - [26/Feb/2008:06:14:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +131.111.47.93 - - [26/Feb/2008:06:16:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.41.139.41 - - [26/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.235.1.29 - - [26/Feb/2008:06:25:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.235.1.29 - - [26/Feb/2008:06:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.235.1.29 - - [26/Feb/2008:06:25:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:30:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +87.244.30.18 - - [26/Feb/2008:06:30:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.221.19.2 - - [26/Feb/2008:06:32:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.221.19.2 - - [26/Feb/2008:06:32:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 21275 +219.64.4.24 - - [26/Feb/2008:06:34:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +219.64.4.24 - - [26/Feb/2008:06:34:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqHPUXSharedLibraries HTTP/1.0" 200 3683 +62.153.70.82 - - [26/Feb/2008:06:40:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:06:40:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:06:40:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:42:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +193.213.227.243 - - [26/Feb/2008:06:42:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +84.110.129.69 - - [26/Feb/2008:06:42:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.129.69 - - [26/Feb/2008:06:42:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 +203.73.43.189 - - [26/Feb/2008:06:46:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.58.194 - - [26/Feb/2008:06:48:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.24.58.194 - - [26/Feb/2008:06:48:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply HTTP/1.1" 301 242 +141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.67.152.241 - - [26/Feb/2008:06:52:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.67.152.241 - - [26/Feb/2008:06:52:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 243188 +71.57.91.136 - - [26/Feb/2008:06:54:44 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 +141.84.220.111 - - [26/Feb/2008:06:55:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.198.156.6 - - [26/Feb/2008:06:55:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +199.111.224.90 - - [26/Feb/2008:06:57:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:24 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.224.90 - - [26/Feb/2008:07:01:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:01:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.226.179.4 - - [26/Feb/2008:07:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +194.226.179.4 - - [26/Feb/2008:07:02:19 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +194.226.179.4 - - [26/Feb/2008:07:02:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.200.218.2 - - [26/Feb/2008:07:03:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +85.179.0.240 - - [26/Feb/2008:07:03:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.179.0.240 - - [26/Feb/2008:07:04:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.224.90 - - [26/Feb/2008:07:04:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.224.90 - - [26/Feb/2008:07:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.134 - - [26/Feb/2008:07:08:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1236 +137.226.57.203 - - [26/Feb/2008:07:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:07:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.146.189.12 - - [26/Feb/2008:07:10:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +62.153.70.82 - - [26/Feb/2008:07:14:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:15:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.197.76.25 - - [26/Feb/2008:07:22:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:23:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.197.76.25 - - [26/Feb/2008:07:23:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +159.149.89.40 - - [26/Feb/2008:07:24:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +217.197.76.25 - - [26/Feb/2008:07:26:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.224.90 - - [26/Feb/2008:07:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.22 - - [26/Feb/2008:07:43:23 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 +199.111.224.90 - - [26/Feb/2008:07:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:46:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:49:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.246.129.196 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +81.80.245.157 - - [26/Feb/2008:07:54:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +66.232.113.194 - - [26/Feb/2008:07:55:43 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +200.51.41.29 - - [26/Feb/2008:07:55:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +161.53.125.15 - - [26/Feb/2008:07:55:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +218.109.113.209 - - [26/Feb/2008:07:57:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.109.113.209 - - [26/Feb/2008:07:57:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +64.22.160.1 - - [26/Feb/2008:07:57:55 -0600] "GET / HTTP/1.1" 200 4447 +64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +121.95.130.217 - - [26/Feb/2008:07:58:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.245.137.240 - - [26/Feb/2008:08:00:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.137.240 - - [26/Feb/2008:08:00:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.137.240 - - [26/Feb/2008:08:00:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET / HTTP/1.1" 200 4447 +77.91.224.3 - - [26/Feb/2008:08:10:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.170.204.27 - - [26/Feb/2008:08:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.170.204.27 - - [26/Feb/2008:08:11:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.170.204.27 - - [26/Feb/2008:08:11:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +64.233.178.136 - - [26/Feb/2008:08:11:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.170.204.27 - - [26/Feb/2008:08:12:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +199.111.224.90 - - [26/Feb/2008:08:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.74.202.25 - - [26/Feb/2008:08:12:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.205.208.176 - - [26/Feb/2008:08:15:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +75.205.208.176 - - [26/Feb/2008:08:15:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +217.65.240.234 - - [26/Feb/2008:08:18:17 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:08:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.84.68.242 - - [26/Feb/2008:08:29:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +75.205.208.176 - - [26/Feb/2008:08:30:00 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 1373410 +87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.221.197.20 - - [26/Feb/2008:08:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +59.103.3.200 - - [26/Feb/2008:08:33:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +206.51.237.114 - - [26/Feb/2008:08:33:01 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +59.103.3.200 - - [26/Feb/2008:08:33:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +196.207.40.212 - - [26/Feb/2008:08:33:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +143.248.134.61 - - [26/Feb/2008:08:33:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.179.223.243 - - [26/Feb/2008:08:33:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +89.179.223.243 - - [26/Feb/2008:08:34:02 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1296 +202.160.174.4 - - [26/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [26/Feb/2008:08:37:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +202.160.174.4 - - [26/Feb/2008:08:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.205.212.172 - - [26/Feb/2008:08:42:00 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +193.205.212.172 - - [26/Feb/2008:08:42:05 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/ApiFreeze HTTP/1.1" 200 4108 +193.205.212.172 - - [26/Feb/2008:08:42:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +193.205.212.172 - - [26/Feb/2008:08:42:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +193.205.212.172 - - [26/Feb/2008:08:42:26 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [26/Feb/2008:08:43:23 -0600] "GET /ply/example.html HTTP/1.1" 304 - +200.21.209.183 - - [26/Feb/2008:08:45:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 +200.21.209.183 - - [26/Feb/2008:08:47:18 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.156.57.189 - - [26/Feb/2008:08:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +213.156.57.189 - - [26/Feb/2008:08:49:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +137.226.57.203 - - [26/Feb/2008:08:50:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:08:50:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:08:50:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.23.124.137 - - [26/Feb/2008:08:50:41 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 206 3623122 +88.112.138.119 - - [26/Feb/2008:08:53:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.112.138.119 - - [26/Feb/2008:08:53:32 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +130.236.182.96 - - [26/Feb/2008:08:55:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.35.216.121 - - [26/Feb/2008:09:03:17 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +82.35.216.121 - - [26/Feb/2008:09:03:19 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 +82.35.216.121 - - [26/Feb/2008:09:03:21 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 +82.35.216.121 - - [26/Feb/2008:09:03:33 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=365 HTTP/1.1" 200 9413 +82.35.216.121 - - [26/Feb/2008:09:03:40 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.190.253.151 - - [26/Feb/2008:09:12:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +67.195.44.110 - - [26/Feb/2008:09:12:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +216.171.98.77 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.85.1.1 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.85.1.1 - - [26/Feb/2008:09:19:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:09:22:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +98.193.69.179 - - [26/Feb/2008:09:24:22 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +150.210.155.167 - - [26/Feb/2008:09:26:51 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:09:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [26/Feb/2008:09:36:05 -0600] "GET /robots.txt HTTP/1.0" 200 71 +202.160.179.46 - - [26/Feb/2008:09:36:13 -0600] "GET /ply/ HTTP/1.0" 304 - +138.100.217.162 - - [26/Feb/2008:09:39:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.160.172.70 - - [26/Feb/2008:09:39:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.160.172.70 - - [26/Feb/2008:09:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.217.162 - - [26/Feb/2008:09:39:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +192.54.144.229 - - [26/Feb/2008:09:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [26/Feb/2008:09:42:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +208.22.104.18 - - [26/Feb/2008:09:42:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +148.87.1.172 - - [26/Feb/2008:09:43:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +148.87.1.172 - - [26/Feb/2008:09:44:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +201.236.226.90 - - [26/Feb/2008:09:44:34 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [26/Feb/2008:09:44:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:09:44:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +64.156.216.96 - - [26/Feb/2008:09:44:57 -0600] "GET /python.html HTTP/1.1" 200 18870 +64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +63.239.69.1 - - [26/Feb/2008:09:47:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +63.239.69.1 - - [26/Feb/2008:09:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +64.156.216.96 - - [26/Feb/2008:09:48:33 -0600] "GET /training.html HTTP/1.1" 200 6154 +204.246.129.196 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [26/Feb/2008:09:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.24.76 - - [26/Feb/2008:09:51:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +63.239.69.1 - - [26/Feb/2008:09:53:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +63.239.69.1 - - [26/Feb/2008:09:53:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +63.239.69.1 - - [26/Feb/2008:09:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +63.239.69.1 - - [26/Feb/2008:09:54:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +67.195.58.174 - - [26/Feb/2008:09:54:12 -0600] "GET /ply/ HTTP/1.0" 304 - +63.239.69.1 - - [26/Feb/2008:09:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleLanguageSharedLibraries HTTP/1.1" 200 2600 +216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.75 - - [26/Feb/2008:09:55:13 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.0" 304 - +89.128.52.247 - - [26/Feb/2008:09:55:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.128.52.247 - - [26/Feb/2008:09:55:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.128.52.247 - - [26/Feb/2008:09:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [26/Feb/2008:10:04:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.117 - - [26/Feb/2008:10:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [26/Feb/2008:10:09:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 200 990 +192.54.144.229 - - [26/Feb/2008:10:10:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /python.html HTTP/1.1" 200 18870 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +18.188.70.114 - - [26/Feb/2008:10:15:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +63.239.69.1 - - [26/Feb/2008:10:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +68.145.52.154 - - [26/Feb/2008:10:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.145.52.154 - - [26/Feb/2008:10:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.187.18 - - [26/Feb/2008:10:26:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.121.187.18 - - [26/Feb/2008:10:26:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +80.121.187.18 - - [26/Feb/2008:10:26:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.187.18 - - [26/Feb/2008:10:27:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.172.19.20 - - [26/Feb/2008:10:30:51 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +64.175.40.146 - - [26/Feb/2008:10:31:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.169.46.98 - - [26/Feb/2008:10:31:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.41.243.144 - - [26/Feb/2008:10:31:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +122.117.168.219 - - [26/Feb/2008:10:32:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [26/Feb/2008:10:33:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.191.234.70 - - [26/Feb/2008:10:37:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +216.191.234.70 - - [26/Feb/2008:10:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +216.191.234.70 - - [26/Feb/2008:10:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +38.104.0.30 - - [26/Feb/2008:10:43:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [26/Feb/2008:10:43:24 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.223.13.234 - - [26/Feb/2008:10:44:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +76.223.13.234 - - [26/Feb/2008:10:44:09 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +18.188.70.114 - - [26/Feb/2008:10:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.230.107 - - [26/Feb/2008:10:47:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:10:51:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [26/Feb/2008:10:51:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:10:53:46 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +141.35.1.57 - - [26/Feb/2008:10:54:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +141.35.1.57 - - [26/Feb/2008:10:54:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +141.35.1.57 - - [26/Feb/2008:10:54:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +141.35.1.57 - - [26/Feb/2008:10:55:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +141.35.1.57 - - [26/Feb/2008:10:56:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.0" 200 2725 +128.143.230.107 - - [26/Feb/2008:10:57:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.177.109.221 - - [26/Feb/2008:11:01:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +217.196.43.134 - - [26/Feb/2008:11:05:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.127.17.194 - - [26/Feb/2008:11:06:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.127.17.194 - - [26/Feb/2008:11:06:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 69278 +205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 +205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +194.117.40.162 - - [26/Feb/2008:11:11:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +24.7.210.64 - - [26/Feb/2008:11:12:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [26/Feb/2008:11:13:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:23 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +149.65.130.31 - - [26/Feb/2008:11:13:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 997 +24.1.159.241 - - [26/Feb/2008:11:16:40 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:17:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:17:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply HTTP/1.1" 301 242 +204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [26/Feb/2008:11:23:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:11:23:28 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:11:23:31 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:11:23:32 -0600] "GET / HTTP/1.1" 200 4447 +84.233.245.68 - - [26/Feb/2008:11:24:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +84.233.245.68 - - [26/Feb/2008:11:24:42 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +84.233.245.68 - - [26/Feb/2008:11:25:19 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1775 +84.233.245.68 - - [26/Feb/2008:11:25:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +84.233.245.68 - - [26/Feb/2008:11:25:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.113.160.194 - - [26/Feb/2008:11:30:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +38.113.160.194 - - [26/Feb/2008:11:30:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:31:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:31:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.125.239 - - [26/Feb/2008:11:31:28 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +38.113.160.194 - - [26/Feb/2008:11:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +38.113.160.194 - - [26/Feb/2008:11:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +129.42.208.182 - - [26/Feb/2008:11:39:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.42.208.182 - - [26/Feb/2008:11:39:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.0.246.111 - - [26/Feb/2008:11:47:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.0.246.111 - - [26/Feb/2008:11:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:51:14 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +74.6.25.20 - - [26/Feb/2008:11:57:01 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.23.225 - - [26/Feb/2008:11:57:01 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +72.22.5.152 - - [26/Feb/2008:11:57:40 -0600] "GET /cv.html HTTP/1.1" 200 31798 +195.212.29.92 - - [26/Feb/2008:11:59:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:22 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:37 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +24.15.187.198 - - [26/Feb/2008:12:00:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +129.42.208.182 - - [26/Feb/2008:12:02:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET / HTTP/1.1" 200 4447 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.204.246 - - [26/Feb/2008:12:14:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.204.246 - - [26/Feb/2008:12:14:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 +195.131.84.202 - - [26/Feb/2008:12:17:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +195.131.84.219 - - [26/Feb/2008:12:17:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.3.45.46 - - [26/Feb/2008:12:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.131.84.251 - - [26/Feb/2008:12:19:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.152.228 - - [26/Feb/2008:12:23:05 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 +128.135.152.228 - - [26/Feb/2008:12:23:36 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 +128.135.152.228 - - [26/Feb/2008:12:24:44 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 133908 +128.135.152.228 - - [26/Feb/2008:12:25:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.202.49.172 - - [26/Feb/2008:12:33:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [26/Feb/2008:12:34:04 -0600] "GET / HTTP/1.1" 200 4447 +67.195.44.110 - - [26/Feb/2008:12:35:28 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [26/Feb/2008:12:35:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 +38.113.160.194 - - [26/Feb/2008:12:38:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +38.113.160.194 - - [26/Feb/2008:12:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +38.113.160.194 - - [26/Feb/2008:12:38:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +24.15.187.198 - - [26/Feb/2008:12:38:54 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +24.15.187.198 - - [26/Feb/2008:12:39:17 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:39:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +72.30.226.134 - - [26/Feb/2008:12:40:09 -0600] "GET /ply/ HTTP/1.0" 200 8018 +64.3.45.46 - - [26/Feb/2008:12:40:43 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +24.15.187.198 - - [26/Feb/2008:12:40:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [26/Feb/2008:12:40:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [26/Feb/2008:12:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [26/Feb/2008:12:43:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +216.148.212.222 - - [26/Feb/2008:12:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [26/Feb/2008:12:44:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.157.220.80 - - [26/Feb/2008:12:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.3.45.46 - - [26/Feb/2008:12:47:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +64.3.45.46 - - [26/Feb/2008:12:47:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /photos/u505/pages/IMG_1514.htm HTTP/1.0" 404 133 +65.54.99.91 - - [26/Feb/2008:12:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 64334 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 66002 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET / HTTP/1.1" 200 4447 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 +65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 74610 +24.15.187.198 - - [26/Feb/2008:12:51:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:52:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:52:13 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +195.252.125.134 - - [26/Feb/2008:12:52:13 -0600] "GET /swig/swig-1.3.28rc2.tar.gz HTTP/1.1" 200 3972567 +24.15.187.198 - - [26/Feb/2008:12:52:30 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:52:39 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [26/Feb/2008:12:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:56:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +13.13.16.1 - - [26/Feb/2008:12:58:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - +129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +129.42.208.182 - - [26/Feb/2008:12:59:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.27.16 - - [26/Feb/2008:13:11:51 -0600] "GET /software.html HTTP/1.0" 304 - +148.241.78.63 - - [26/Feb/2008:13:12:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.78.63 - - [26/Feb/2008:13:12:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.78.63 - - [26/Feb/2008:13:12:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.109.126 - - [26/Feb/2008:13:13:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +161.45.109.126 - - [26/Feb/2008:13:14:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:14:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.109.126 - - [26/Feb/2008:13:14:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:15:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:18:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.218.181 - - [26/Feb/2008:13:23:08 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.218.181 - - [26/Feb/2008:13:23:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +89.165.73.169 - - [26/Feb/2008:13:32:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.191.234.70 - - [26/Feb/2008:13:33:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +216.191.234.70 - - [26/Feb/2008:13:33:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/index.html HTTP/1.1" 200 40215 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/hello.gif HTTP/1.1" 200 1118 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame0.gif HTTP/1.1" 200 432 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/gp.gif HTTP/1.1" 200 43556 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame1.gif HTTP/1.1" 200 2588 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame2.gif HTTP/1.1" 200 2237 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/clip.gif HTTP/1.1" 200 21566 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/plot2d.gif HTTP/1.1" 200 5902 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d.gif HTTP/1.1" 200 2212 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_2.gif HTTP/1.1" 200 888 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_3.gif HTTP/1.1" 200 1194 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_4.gif HTTP/1.1" 200 811 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_5.gif HTTP/1.1" 200 1223 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_6.gif HTTP/1.1" 200 1870 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_1.gif HTTP/1.1" 200 16420 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_2.gif HTTP/1.1" 200 34142 +64.113.185.61 - - [26/Feb/2008:13:40:14 -0600] "GET /gifplot/plot3d_3.gif HTTP/1.1" 200 55793 +128.221.197.20 - - [26/Feb/2008:13:42:31 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +148.241.78.63 - - [26/Feb/2008:13:47:47 -0600] "GET /ply/ HTTP/1.1" 304 - +148.241.78.63 - - [26/Feb/2008:13:47:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +88.90.231.185 - - [26/Feb/2008:13:48:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.125.245 - - [26/Feb/2008:13:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.245 - - [26/Feb/2008:13:52:11 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +216.54.141.250 - - [26/Feb/2008:13:52:56 -0600] "GET /python.html HTTP/1.1" 200 18870 +216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.16.64.124 - - [26/Feb/2008:13:55:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.16.64.124 - - [26/Feb/2008:13:55:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.16.64.124 - - [26/Feb/2008:13:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.16.64.124 - - [26/Feb/2008:13:55:54 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.109.126 - - [26/Feb/2008:13:59:38 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.109.126 - - [26/Feb/2008:14:06:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 +89.165.73.169 - - [26/Feb/2008:14:08:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:14:19:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.78.63 - - [26/Feb/2008:14:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.151 - - [26/Feb/2008:14:21:41 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +88.111.158.229 - - [26/Feb/2008:14:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [26/Feb/2008:14:25:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [26/Feb/2008:14:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [26/Feb/2008:14:30:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.22.104.18 - - [26/Feb/2008:14:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +128.221.197.20 - - [26/Feb/2008:14:30:42 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +208.22.104.18 - - [26/Feb/2008:14:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleInputTypemaps HTTP/1.1" 200 2300 +90.185.65.84 - - [26/Feb/2008:14:31:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +208.22.104.18 - - [26/Feb/2008:14:31:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +208.22.104.18 - - [26/Feb/2008:14:32:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +208.22.104.18 - - [26/Feb/2008:14:34:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +208.22.104.18 - - [26/Feb/2008:14:34:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +208.22.104.18 - - [26/Feb/2008:14:35:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +67.195.58.172 - - [26/Feb/2008:14:42:14 -0600] "GET /index.html HTTP/1.0" 304 - +80.91.229.6 - - [26/Feb/2008:14:44:58 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 +80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET / HTTP/1.1" 200 4447 +129.21.160.34 - - [26/Feb/2008:14:47:48 -0600] "GET / HTTP/1.1" 200 4447 +128.221.197.20 - - [26/Feb/2008:14:50:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.164.42.125 - - [26/Feb/2008:14:53:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.164.42.125 - - [26/Feb/2008:14:53:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +77.91.224.3 - - [26/Feb/2008:14:54:07 -0600] "GET /software.html HTTP/1.1" 200 3163 +74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.164.42.125 - - [26/Feb/2008:14:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /training.html HTTP/1.1" 200 6154 +68.164.42.125 - - [26/Feb/2008:14:54:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic HTTP/1.1" 301 246 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.120.207.254 - - [26/Feb/2008:14:55:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +200.21.98.7 - - [26/Feb/2008:14:55:48 -0600] "GET / HTTP/1.0" 200 4447 +200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +209.120.207.254 - - [26/Feb/2008:14:55:58 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +84.110.202.119 - - [26/Feb/2008:14:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.202.119 - - [26/Feb/2008:14:56:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +77.91.224.3 - - [26/Feb/2008:14:56:17 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +171.161.224.10 - - [26/Feb/2008:14:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:14:57:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +77.91.224.13 - - [26/Feb/2008:14:57:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 +77.91.224.3 - - [26/Feb/2008:14:59:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +77.91.224.13 - - [26/Feb/2008:15:00:09 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.235 - - [26/Feb/2008:15:01:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +200.21.98.7 - - [26/Feb/2008:15:02:31 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 31530 +139.140.198.50 - - [26/Feb/2008:15:06:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 189268 +82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:14:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.135.12.203 - - [26/Feb/2008:15:14:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +82.135.12.203 - - [26/Feb/2008:15:14:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +86.157.119.197 - - [26/Feb/2008:15:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:19:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.135.12.203 - - [26/Feb/2008:15:19:25 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.135.12.203 - - [26/Feb/2008:15:19:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +24.10.16.193 - - [26/Feb/2008:15:19:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.135.12.203 - - [26/Feb/2008:15:19:38 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:20:21 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4233 +24.10.16.193 - - [26/Feb/2008:15:23:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.10.16.193 - - [26/Feb/2008:15:23:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +88.112.138.119 - - [26/Feb/2008:15:23:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET / HTTP/1.1" 200 4447 +192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /about.html HTTP/1.1" 200 7890 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +192.55.2.36 - - [26/Feb/2008:15:24:46 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +192.55.2.36 - - [26/Feb/2008:15:24:51 -0600] "GET /diet.html HTTP/1.1" 404 133 +192.55.2.36 - - [26/Feb/2008:15:25:02 -0600] "GET /index.html HTTP/1.1" 200 4447 +88.191.19.81 - - [26/Feb/2008:15:31:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic HTTP/1.1" 301 246 +209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.235 - - [26/Feb/2008:15:33:35 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +139.11.6.202 - - [26/Feb/2008:15:37:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +139.11.6.202 - - [26/Feb/2008:15:38:13 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +139.11.6.202 - - [26/Feb/2008:15:38:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +139.11.6.202 - - [26/Feb/2008:15:38:27 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 +139.11.6.202 - - [26/Feb/2008:15:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +139.11.6.202 - - [26/Feb/2008:15:38:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.0" 200 1927 +139.11.6.202 - - [26/Feb/2008:15:38:45 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.0" 200 4493 +139.11.6.202 - - [26/Feb/2008:15:38:51 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.0" 200 1579 +139.11.6.202 - - [26/Feb/2008:15:38:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.0" 200 8981 +66.249.65.37 - - [26/Feb/2008:15:39:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [26/Feb/2008:15:39:17 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 304 - +82.41.78.129 - - [26/Feb/2008:15:43:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.41.78.129 - - [26/Feb/2008:15:43:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +88.112.138.119 - - [26/Feb/2008:15:44:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.69.70.197 - - [26/Feb/2008:15:46:01 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +79.69.70.197 - - [26/Feb/2008:15:46:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.41.78.129 - - [26/Feb/2008:15:48:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.19.115 - - [26/Feb/2008:15:49:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +146.189.58.99 - - [26/Feb/2008:15:53:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +146.189.58.99 - - [26/Feb/2008:15:53:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +146.189.58.99 - - [26/Feb/2008:15:53:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +146.189.58.99 - - [26/Feb/2008:15:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +85.228.229.73 - - [26/Feb/2008:15:55:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.228.229.73 - - [26/Feb/2008:15:55:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.228.229.73 - - [26/Feb/2008:15:55:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +85.228.229.73 - - [26/Feb/2008:15:56:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +85.228.229.73 - - [26/Feb/2008:15:56:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 +38.98.120.84 - - [26/Feb/2008:15:56:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:15:56:15 -0600] "GET / HTTP/1.1" 200 4447 +216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.9.243.111 - - [26/Feb/2008:15:57:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [26/Feb/2008:15:58:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +216.9.243.111 - - [26/Feb/2008:15:58:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 304 - +128.135.125.239 - - [26/Feb/2008:15:59:12 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - +216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +208.80.193.45 - - [26/Feb/2008:16:00:46 -0600] "GET / HTTP/1.1" 200 4447 +89.128.216.67 - - [26/Feb/2008:16:01:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.128.216.67 - - [26/Feb/2008:16:01:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.128.216.67 - - [26/Feb/2008:16:01:24 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +128.8.114.84 - - [26/Feb/2008:16:03:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +128.8.114.84 - - [26/Feb/2008:16:03:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.8.114.84 - - [26/Feb/2008:16:03:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [26/Feb/2008:16:06:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +208.97.218.10 - - [26/Feb/2008:16:06:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.249.65.37 - - [26/Feb/2008:16:09:51 -0600] "GET /software.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:11 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:15 -0600] "GET /ply/README HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:34 -0600] "GET /ply/?ref=%C4%B0lkSexShop.Com HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:11:18 -0600] "GET /writing.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:11:29 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:06 -0600] "GET /ply/ HTTP/1.1" 304 - +128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.100.195.84 - - [26/Feb/2008:16:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/assign1.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:30 -0600] "GET /consulting.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:49 -0600] "GET /ply HTTP/1.1" 301 242 +66.249.65.37 - - [26/Feb/2008:16:12:58 -0600] "GET /ply/index.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:13:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.195.84 - - [26/Feb/2008:16:14:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 212696 +67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 +67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 +67.173.185.186 - - [26/Feb/2008:16:16:05 -0600] "GET / HTTP/1.1" 200 4447 +67.173.185.186 - - [26/Feb/2008:16:16:06 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.173.185.186 - - [26/Feb/2008:16:16:42 -0600] "GET /assign5.html HTTP/1.1" 404 133 +74.6.23.209 - - [26/Feb/2008:16:16:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1231 +67.173.185.186 - - [26/Feb/2008:16:16:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +68.12.248.157 - - [26/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.16 - - [26/Feb/2008:16:24:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [26/Feb/2008:16:24:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - +128.100.195.84 - - [26/Feb/2008:16:25:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 +68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:16:33:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:16:33:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +190.24.33.53 - - [26/Feb/2008:16:34:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.233.178.136 - - [26/Feb/2008:16:35:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 +89.170.38.243 - - [26/Feb/2008:16:36:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:16:39:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.133.9.141 - - [26/Feb/2008:16:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +199.111.224.90 - - [26/Feb/2008:16:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.133.9.141 - - [26/Feb/2008:16:53:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.133.9.141 - - [26/Feb/2008:16:53:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +199.111.224.90 - - [26/Feb/2008:16:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.198.216.189 - - [26/Feb/2008:16:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +199.198.216.189 - - [26/Feb/2008:16:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +210.143.35.17 - - [26/Feb/2008:16:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:17:05:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:17:05:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.167.244.66 - - [26/Feb/2008:17:08:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/ HTTP/1.1" 304 - +202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +202.226.91.246 - - [26/Feb/2008:17:17:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /ply HTTP/1.1" 301 242 +67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET / HTTP/1.1" 200 4447 +67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET /index.html HTTP/1.1" 200 4447 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /software.html HTTP/1.1" 200 3163 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /about.html HTTP/1.1" 200 7890 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /python.html HTTP/1.1" 200 18870 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /cv.html HTTP/1.1" 200 31798 +67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /syllabus.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:38 -0600] "GET /01Introduction.pdf HTTP/1.1" 200 2172146 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign1.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /soln1.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /02WorkingWithData.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign2.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /smackdown.py HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /03ProgramStructure.pdf HTTP/1.1" 200 279926 +67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /assign3.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:42 -0600] "GET /04Objects.pdf HTTP/1.1" 200 502854 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /05ObjectModel.pdf HTTP/1.1" 200 719628 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign4.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /06FilesAndText.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign5.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /07Functional.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /ply.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /example.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /README HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /PLYTalk.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /support.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /exec.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Usenix2001/beazley.pdf HTTP/1.1" 200 76713 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +67.228.115.170 - - [26/Feb/2008:17:32:47 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.1" 200 82126 +67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +67.228.115.170 - - [26/Feb/2008:17:32:49 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +67.228.115.170 - - [26/Feb/2008:17:32:50 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 +67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +67.228.115.170 - - [26/Feb/2008:17:32:52 -0600] "GET /swigperl.pdf HTTP/1.1" 404 133 +128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.239.253 - - [26/Feb/2008:17:33:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [26/Feb/2008:17:34:47 -0600] "GET /ply/ HTTP/1.0" 304 - +203.144.160.249 - - [26/Feb/2008:17:38:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +67.195.58.182 - - [26/Feb/2008:17:38:50 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 304 - +67.195.58.186 - - [26/Feb/2008:17:38:53 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - +128.143.136.157 - - [26/Feb/2008:17:39:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.144.160.249 - - [26/Feb/2008:17:39:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +203.144.160.249 - - [26/Feb/2008:17:39:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +67.195.58.151 - - [26/Feb/2008:17:39:32 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 304 - +67.195.58.170 - - [26/Feb/2008:17:39:38 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 304 - +203.144.160.249 - - [26/Feb/2008:17:39:46 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +67.195.58.181 - - [26/Feb/2008:17:40:14 -0600] "GET /ply/support.html HTTP/1.0" 304 - +67.195.58.170 - - [26/Feb/2008:17:40:17 -0600] "GET / HTTP/1.0" 304 - +67.195.58.177 - - [26/Feb/2008:17:41:00 -0600] "GET /python.html HTTP/1.0" 200 18870 +67.195.58.185 - - [26/Feb/2008:17:41:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +128.143.136.157 - - [26/Feb/2008:17:41:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:17:41:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.168 - - [26/Feb/2008:17:42:31 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.0" 404 133 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.114.59.172 - - [26/Feb/2008:17:46:55 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +199.46.245.233 - - [26/Feb/2008:17:51:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.114.59.172 - - [26/Feb/2008:17:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +69.213.243.113 - - [26/Feb/2008:17:54:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.195.58.164 - - [26/Feb/2008:17:55:03 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 304 - +69.213.243.113 - - [26/Feb/2008:17:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +69.213.243.113 - - [26/Feb/2008:17:57:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +69.213.243.113 - - [26/Feb/2008:17:58:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +74.6.22.11 - - [26/Feb/2008:18:02:12 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 +74.6.28.205 - - [26/Feb/2008:18:03:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /python.html HTTP/1.1" 200 18870 +216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +216.31.211.11 - - [26/Feb/2008:18:03:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:18:03:36 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +217.196.43.134 - - [26/Feb/2008:18:05:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.22.217 - - [26/Feb/2008:18:17:27 -0600] "GET /dynamic/04Objects.pdf HTTP/1.0" 200 514533 +67.195.58.168 - - [26/Feb/2008:18:19:04 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 304 - +74.6.20.14 - - [26/Feb/2008:18:19:37 -0600] "GET /tenure/trip3.html HTTP/1.0" 404 133 +87.194.101.28 - - [26/Feb/2008:18:28:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.194.101.28 - - [26/Feb/2008:18:29:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12329 +87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.101.28 - - [26/Feb/2008:18:29:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE047.HTM HTTP/1.1" 304 - +128.143.136.157 - - [26/Feb/2008:18:35:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:35:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.86.204.99 - - [26/Feb/2008:18:39:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +38.98.120.84 - - [26/Feb/2008:18:39:55 -0600] "GET / HTTP/1.1" 200 4447 +65.57.245.11 - - [26/Feb/2008:18:40:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +66.232.113.194 - - [26/Feb/2008:18:43:36 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +130.86.204.99 - - [26/Feb/2008:18:43:43 -0600] "GET /ply/README HTTP/1.1" 200 8605 +125.244.152.66 - - [26/Feb/2008:18:43:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +202.216.177.14 - - [26/Feb/2008:18:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +85.185.11.131 - - [26/Feb/2008:18:43:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +216.148.212.222 - - [26/Feb/2008:18:50:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.145 - - [26/Feb/2008:18:56:31 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +206.51.237.114 - - [26/Feb/2008:19:02:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +213.29.144.10 - - [26/Feb/2008:19:02:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:19:13:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE067.HTM HTTP/1.1" 304 - +65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /photos/u505/pages/IMG_1517.htm HTTP/1.1" 404 133 +67.195.34.108 - - [26/Feb/2008:19:18:09 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +67.195.34.114 - - [26/Feb/2008:19:19:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:21:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:22:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.23.73 - - [26/Feb/2008:19:24:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 +62.59.179.107 - - [26/Feb/2008:19:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.79.38.12 - - [26/Feb/2008:19:27:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.79.38.12 - - [26/Feb/2008:19:27:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.79.38.12 - - [26/Feb/2008:19:28:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.58.86.1 - - [26/Feb/2008:19:30:50 -0600] "GET /dynamic/ HTTP/1.1" 304 - +74.6.26.171 - - [26/Feb/2008:19:30:51 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply/ HTTP/1.0" 200 8018 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.167.150 - - [26/Feb/2008:19:32:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +62.59.179.107 - - [26/Feb/2008:19:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:19:36:30 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +201.83.27.70 - - [26/Feb/2008:19:38:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:39:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:39:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +201.83.27.70 - - [26/Feb/2008:19:39:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.205.10 - - [26/Feb/2008:19:45:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.202.49.172 - - [26/Feb/2008:19:45:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 +128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:56:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:58:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:58:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.91.57 - - [26/Feb/2008:20:00:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.91.57 - - [26/Feb/2008:20:00:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.19.236 - - [26/Feb/2008:20:04:05 -0600] "GET /training.html HTTP/1.0" 200 6154 +62.59.179.107 - - [26/Feb/2008:20:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [26/Feb/2008:20:06:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +202.160.180.213 - - [26/Feb/2008:20:07:22 -0600] "GET / HTTP/1.0" 200 4447 +76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.189.146.60 - - [26/Feb/2008:20:10:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +76.189.146.60 - - [26/Feb/2008:20:11:10 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +76.189.146.60 - - [26/Feb/2008:20:11:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +76.189.146.60 - - [26/Feb/2008:20:11:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:20:12:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +192.94.38.34 - - [26/Feb/2008:20:16:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.130.247.245 - - [26/Feb/2008:20:27:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.249.147.81 - - [26/Feb/2008:20:28:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.94.38.34 - - [26/Feb/2008:20:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +192.94.38.34 - - [26/Feb/2008:20:29:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1631 +192.94.38.34 - - [26/Feb/2008:20:29:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +65.214.45.129 - - [26/Feb/2008:20:35:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply HTTP/1.1" 301 242 +205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:41:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.46.29.140 - - [26/Feb/2008:20:45:10 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2739 +219.87.152.215 - - [26/Feb/2008:20:45:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +69.217.73.52 - - [26/Feb/2008:20:45:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +80.97.94.178 - - [26/Feb/2008:20:45:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.63.117.142 - - [26/Feb/2008:20:45:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +99.232.163.8 - - [26/Feb/2008:20:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [26/Feb/2008:20:45:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.195.58.174 - - [26/Feb/2008:20:46:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 304 - +67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/example.html HTTP/1.0" 304 - +67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 304 - +67.195.58.178 - - [26/Feb/2008:20:47:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 304 - +67.176.147.11 - - [26/Feb/2008:20:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.176.147.11 - - [26/Feb/2008:20:51:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [26/Feb/2008:20:52:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [26/Feb/2008:20:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.249.52.137 - - [26/Feb/2008:20:54:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.249.52.137 - - [26/Feb/2008:20:54:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:16 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +203.73.43.189 - - [26/Feb/2008:20:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.189.93.50 - - [26/Feb/2008:21:00:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.189.93.50 - - [26/Feb/2008:21:01:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.110.229.30 - - [26/Feb/2008:21:04:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.229.30 - - [26/Feb/2008:21:04:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +82.249.138.163 - - [26/Feb/2008:21:07:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.249.138.163 - - [26/Feb/2008:21:07:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.252.149.15 - - [26/Feb/2008:21:08:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [26/Feb/2008:21:08:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.3.173.130 - - [26/Feb/2008:21:12:25 -0600] "GET /ply/ HTTP/1.1" 304 - +195.3.173.130 - - [26/Feb/2008:21:12:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +195.3.173.130 - - [26/Feb/2008:21:12:43 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 61381 +98.206.164.173 - - [26/Feb/2008:21:16:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +98.206.164.173 - - [26/Feb/2008:21:16:26 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +69.219.161.183 - - [26/Feb/2008:21:19:43 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:21:20:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +99.232.163.8 - - [26/Feb/2008:21:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:21:21:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.7.107 - - [26/Feb/2008:21:24:19 -0600] "GET /swill/ HTTP/1.0" 200 3786 +201.236.226.90 - - [26/Feb/2008:21:26:23 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [26/Feb/2008:21:26:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [26/Feb/2008:21:26:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:21:26:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 206 23851 +201.236.226.90 - - [26/Feb/2008:21:26:59 -0600] "GET /python.html HTTP/1.1" 200 18870 +201.236.226.90 - - [26/Feb/2008:21:27:03 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [26/Feb/2008:21:28:20 -0600] "GET /python.html HTTP/1.1" 200 18870 +208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.5.217.3 - - [26/Feb/2008:21:36:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.164 - - [26/Feb/2008:21:41:59 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:21:44:56 -0600] "GET / HTTP/1.1" 200 4447 +70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:21:47:53 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +70.113.58.196 - - [26/Feb/2008:21:47:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.14.195.226 - - [26/Feb/2008:21:47:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +204.111.252.233 - - [26/Feb/2008:21:48:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +204.111.252.233 - - [26/Feb/2008:21:48:02 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +203.5.217.3 - - [26/Feb/2008:21:48:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.198.190.123 - - [26/Feb/2008:21:49:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.198.190.123 - - [26/Feb/2008:21:49:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.14.195.226 - - [26/Feb/2008:21:53:56 -0600] "GET /ply/README HTTP/1.1" 200 8605 +68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.165.56.5 - - [26/Feb/2008:21:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.165.56.5 - - [26/Feb/2008:21:54:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.26.147 - - [26/Feb/2008:21:56:50 -0600] "GET /photos/wind/pages/IMG_1331.htm HTTP/1.0" 404 133 +70.113.58.196 - - [26/Feb/2008:22:02:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.250.6.243 - - [26/Feb/2008:22:04:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.250.6.243 - - [26/Feb/2008:22:04:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET / HTTP/1.1" 200 4447 +67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.165.221.14 - - [26/Feb/2008:22:05:51 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.165.221.14 - - [26/Feb/2008:22:06:29 -0600] "GET /software.html HTTP/1.1" 200 3163 +67.165.221.14 - - [26/Feb/2008:22:06:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.165.221.14 - - [26/Feb/2008:22:06:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +67.165.221.14 - - [26/Feb/2008:22:06:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /about.html HTTP/1.1" 200 7890 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +67.165.221.14 - - [26/Feb/2008:22:08:25 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.165.221.14 - - [26/Feb/2008:22:08:31 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +71.145.147.182 - - [26/Feb/2008:22:19:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.145.147.182 - - [26/Feb/2008:22:19:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:22:19:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:22:19:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.145.147.182 - - [26/Feb/2008:22:19:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +128.143.38.176 - - [26/Feb/2008:22:20:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.28.122 - - [26/Feb/2008:22:22:23 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:22:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.228.99 - - [26/Feb/2008:22:34:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:22:39:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 +163.29.130.55 - - [26/Feb/2008:22:39:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.124.85.80 - - [26/Feb/2008:22:40:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.80 - - [26/Feb/2008:22:42:26 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +222.153.72.162 - - [26/Feb/2008:22:43:28 -0600] "GET / HTTP/1.1" 200 4447 +128.143.117.220 - - [26/Feb/2008:22:45:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:22:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [26/Feb/2008:23:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.126.245.198 - - [26/Feb/2008:23:04:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.126.222.62 - - [26/Feb/2008:23:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.126.222.62 - - [26/Feb/2008:23:04:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +74.6.26.212 - - [26/Feb/2008:23:07:59 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.0" 200 85901 +65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET / HTTP/1.0" 200 4447 +65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /training.html HTTP/1.1" 200 6154 +128.250.6.243 - - [26/Feb/2008:23:22:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.143.136.157 - - [26/Feb/2008:23:23:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:23:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.250.6.243 - - [26/Feb/2008:23:26:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.250.6.243 - - [26/Feb/2008:23:26:48 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +128.250.6.243 - - [26/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +217.172.44.82 - - [26/Feb/2008:23:27:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.25.20 - - [26/Feb/2008:23:28:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.176.147.11 - - [26/Feb/2008:23:29:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +67.176.147.11 - - [26/Feb/2008:23:34:13 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.206.164.173 - - [26/Feb/2008:23:44:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +65.214.45.129 - - [26/Feb/2008:23:49:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 +59.93.252.161 - - [26/Feb/2008:23:53:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +59.93.252.161 - - [26/Feb/2008:23:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:43 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +61.11.86.160 - - [27/Feb/2008:00:13:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +61.11.86.160 - - [27/Feb/2008:00:13:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.11.86.160 - - [27/Feb/2008:00:13:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.11.86.160 - - [27/Feb/2008:00:14:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +213.145.165.82 - - [27/Feb/2008:00:15:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.11.86.160 - - [27/Feb/2008:00:17:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.82.115.10 - - [27/Feb/2008:00:26:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.0" 200 188949 +67.195.58.169 - - [27/Feb/2008:00:26:53 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 304 - +74.6.20.121 - - [27/Feb/2008:00:29:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 +67.195.58.175 - - [27/Feb/2008:00:47:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 304 - +65.214.45.129 - - [27/Feb/2008:00:49:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +91.98.137.144 - - [27/Feb/2008:00:52:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.217.40.133 - - [27/Feb/2008:00:52:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +91.98.137.144 - - [27/Feb/2008:00:52:34 -0600] "GET / HTTP/1.1" 200 4447 +213.217.40.133 - - [27/Feb/2008:00:52:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +91.98.137.144 - - [27/Feb/2008:00:52:38 -0600] "GET /training.html HTTP/1.1" 200 6154 +91.98.137.144 - - [27/Feb/2008:00:52:43 -0600] "GET /index.html HTTP/1.1" 200 4447 +91.98.137.144 - - [27/Feb/2008:00:52:52 -0600] "GET /software.html HTTP/1.1" 200 3163 +91.98.137.144 - - [27/Feb/2008:00:53:14 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +91.98.137.144 - - [27/Feb/2008:00:53:19 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 +91.98.137.144 - - [27/Feb/2008:00:53:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +122.164.159.157 - - [27/Feb/2008:00:55:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +122.164.159.157 - - [27/Feb/2008:00:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.39.203 - - [27/Feb/2008:01:03:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +41.232.39.203 - - [27/Feb/2008:01:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [27/Feb/2008:01:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [27/Feb/2008:01:08:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 130588 +69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:01:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.206.102.103 - - [27/Feb/2008:01:16:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.206.102.103 - - [27/Feb/2008:01:16:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.206.102.103 - - [27/Feb/2008:01:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.160 - - [27/Feb/2008:01:22:43 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - +199.111.224.90 - - [27/Feb/2008:01:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [27/Feb/2008:01:28:22 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "HEAD /ply/ HTTP/1.0" 200 0 +38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 +80.121.187.18 - - [27/Feb/2008:01:29:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.121.187.18 - - [27/Feb/2008:01:29:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +80.121.187.18 - - [27/Feb/2008:01:29:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.121.187.18 - - [27/Feb/2008:01:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +80.121.187.18 - - [27/Feb/2008:01:30:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +80.121.187.18 - - [27/Feb/2008:01:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.121.187.18 - - [27/Feb/2008:01:31:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +80.121.187.18 - - [27/Feb/2008:01:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.24.27.20 - - [27/Feb/2008:01:33:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.24.27.20 - - [27/Feb/2008:01:33:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +76.24.27.20 - - [27/Feb/2008:01:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 +65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 304 - +67.195.58.170 - - [27/Feb/2008:01:42:56 -0600] "GET / HTTP/1.0" 304 - +67.195.58.178 - - [27/Feb/2008:01:43:35 -0600] "GET /software.html HTTP/1.0" 200 3163 +67.195.58.185 - - [27/Feb/2008:01:44:22 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +65.55.212.77 - - [27/Feb/2008:01:48:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.20.75 - - [27/Feb/2008:02:00:30 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.0" 304 - +130.76.32.182 - - [27/Feb/2008:02:00:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.76.32.182 - - [27/Feb/2008:02:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.76.32.182 - - [27/Feb/2008:02:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +130.76.32.182 - - [27/Feb/2008:02:00:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +130.76.32.182 - - [27/Feb/2008:02:01:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +130.76.32.182 - - [27/Feb/2008:02:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsDarwin HTTP/1.1" 200 1916 +130.76.32.182 - - [27/Feb/2008:02:01:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /python.html HTTP/1.1" 200 18870 +130.76.32.182 - - [27/Feb/2008:02:06:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +130.76.32.182 - - [27/Feb/2008:02:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +130.76.32.182 - - [27/Feb/2008:02:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +157.99.64.13 - - [27/Feb/2008:02:06:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +157.99.64.13 - - [27/Feb/2008:02:06:45 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +217.65.240.234 - - [27/Feb/2008:02:08:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +130.76.32.182 - - [27/Feb/2008:02:09:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.35.109.3 - - [27/Feb/2008:02:10:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [27/Feb/2008:02:13:14 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1122 +67.195.58.174 - - [27/Feb/2008:02:19:18 -0600] "GET /ply/ HTTP/1.0" 304 - +210.81.80.193 - - [27/Feb/2008:02:29:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.64.8.202 - - [27/Feb/2008:02:30:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [27/Feb/2008:02:32:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.80.245.157 - - [27/Feb/2008:02:36:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [27/Feb/2008:02:37:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +195.212.29.83 - - [27/Feb/2008:02:48:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.213.7.130 - - [27/Feb/2008:02:52:23 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.248.73.112 - - [27/Feb/2008:02:52:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +203.213.7.130 - - [27/Feb/2008:02:54:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.24.233 - - [27/Feb/2008:02:59:02 -0600] "GET /photos/wind/index.htm HTTP/1.0" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +80.149.16.155 - - [27/Feb/2008:03:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.155.221.86 - - [27/Feb/2008:03:14:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.149.251.212 - - [27/Feb/2008:03:16:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +88.149.251.212 - - [27/Feb/2008:03:16:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:03:23:04 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +24.1.159.241 - - [27/Feb/2008:03:24:10 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 +24.1.159.241 - - [27/Feb/2008:03:24:12 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +64.81.229.55 - - [27/Feb/2008:03:26:48 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +64.81.229.55 - - [27/Feb/2008:03:26:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.88.18.65 - - [27/Feb/2008:03:34:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.13.71.67 - - [27/Feb/2008:03:46:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.13.71.67 - - [27/Feb/2008:03:46:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.21.242.91 - - [27/Feb/2008:03:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:48:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +192.54.144.229 - - [27/Feb/2008:03:48:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [27/Feb/2008:03:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.24.228 - - [27/Feb/2008:03:53:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 +221.11.5.180 - - [27/Feb/2008:03:59:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.11.5.180 - - [27/Feb/2008:03:59:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.39.3.92 - - [27/Feb/2008:04:13:23 -0600] "GET / HTTP/1.1" 206 4447 +192.54.144.229 - - [27/Feb/2008:04:16:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.104.101.239 - - [27/Feb/2008:04:16:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 +124.104.101.239 - - [27/Feb/2008:04:17:59 -0600] "GET /python.html HTTP/1.0" 200 18870 +202.248.73.112 - - [27/Feb/2008:04:24:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +81.255.238.189 - - [27/Feb/2008:04:25:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.103.40.50 - - [27/Feb/2008:04:27:54 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +192.44.63.162 - - [27/Feb/2008:04:28:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.210.2.219 - - [27/Feb/2008:04:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.210.2.219 - - [27/Feb/2008:04:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.165.73.3 - - [27/Feb/2008:04:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.103.40.50 - - [27/Feb/2008:04:47:27 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +74.6.20.163 - - [27/Feb/2008:04:51:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 +195.212.29.83 - - [27/Feb/2008:05:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.76.66.64 - - [27/Feb/2008:05:05:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.76.66.64 - - [27/Feb/2008:05:05:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.76.66.64 - - [27/Feb/2008:05:05:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.76.66.64 - - [27/Feb/2008:05:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +90.80.39.41 - - [27/Feb/2008:05:06:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.127.188.96 - - [27/Feb/2008:05:22:11 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.127.188.96 - - [27/Feb/2008:05:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.212.29.75 - - [27/Feb/2008:05:22:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +128.40.255.154 - - [27/Feb/2008:05:33:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:35:37 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4261 +194.97.106.98 - - [27/Feb/2008:05:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.55.52.1 - - [27/Feb/2008:05:35:45 -0600] "GET /ply/ HTTP/1.0" 200 8018 +192.55.52.1 - - [27/Feb/2008:05:35:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +192.55.52.1 - - [27/Feb/2008:05:35:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +192.55.52.1 - - [27/Feb/2008:05:36:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +194.97.106.98 - - [27/Feb/2008:05:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.80.39.41 - - [27/Feb/2008:05:36:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +90.80.39.41 - - [27/Feb/2008:05:37:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +192.55.52.1 - - [27/Feb/2008:05:37:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +90.80.39.41 - - [27/Feb/2008:05:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +90.80.39.41 - - [27/Feb/2008:05:37:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +192.55.52.1 - - [27/Feb/2008:05:40:19 -0600] "GET /ply/README HTTP/1.0" 200 8605 +66.232.113.194 - - [27/Feb/2008:05:41:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2748 +201.55.193.26 - - [27/Feb/2008:05:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +161.53.125.15 - - [27/Feb/2008:05:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +87.101.244.6 - - [27/Feb/2008:05:41:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:42:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:42:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [27/Feb/2008:05:45:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:05:45:49 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 304 - +193.3.39.1 - - [27/Feb/2008:05:48:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.151.18.10 - - [27/Feb/2008:05:49:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.53.125.15 - - [27/Feb/2008:05:54:13 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "GET /cgi-bin/wiki.pl?ImportDirective HTTP/1.1" 200 2215 +193.3.39.1 - - [27/Feb/2008:05:54:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.255.238.189 - - [27/Feb/2008:06:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.51.237.114 - - [27/Feb/2008:06:06:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +60.28.31.194 - - [27/Feb/2008:06:06:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +189.56.68.138 - - [27/Feb/2008:06:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +67.195.58.158 - - [27/Feb/2008:06:25:02 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +222.122.236.43 - - [27/Feb/2008:06:26:38 -0600] "GET /robots.txt HTTP/1.1" 200 71 +90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +90.185.65.84 - - [27/Feb/2008:06:27:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 +220.23.136.184 - - [27/Feb/2008:06:34:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.23.136.184 - - [27/Feb/2008:06:34:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.233.178.136 - - [27/Feb/2008:06:34:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +220.23.136.184 - - [27/Feb/2008:06:35:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +84.110.148.125 - - [27/Feb/2008:06:44:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.148.125 - - [27/Feb/2008:06:44:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +80.169.151.100 - - [27/Feb/2008:06:46:35 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.112.172.10 - - [27/Feb/2008:06:46:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 +193.112.172.10 - - [27/Feb/2008:06:47:18 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +193.112.172.10 - - [27/Feb/2008:06:47:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 +193.112.172.10 - - [27/Feb/2008:06:47:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +203.213.7.133 - - [27/Feb/2008:06:48:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.213.7.133 - - [27/Feb/2008:06:48:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.156.46.53 - - [27/Feb/2008:06:49:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.213.7.133 - - [27/Feb/2008:06:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.133 - - [27/Feb/2008:06:51:16 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +203.213.7.133 - - [27/Feb/2008:06:53:12 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.236.226.90 - - [27/Feb/2008:06:56:56 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [27/Feb/2008:06:56:58 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [27/Feb/2008:06:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +62.181.186.82 - - [27/Feb/2008:07:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +62.181.186.82 - - [27/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +118.172.30.90 - - [27/Feb/2008:07:01:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 80157 +201.24.117.154 - - [27/Feb/2008:07:01:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.24.117.154 - - [27/Feb/2008:07:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.181.186.82 - - [27/Feb/2008:07:03:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.12.88.244 - - [27/Feb/2008:07:13:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:14:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.133.202 - - [27/Feb/2008:07:14:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.13.133.202 - - [27/Feb/2008:07:14:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:15:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [27/Feb/2008:07:15:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:18:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /photos/u505/pages/IMG_1490.htm HTTP/1.0" 404 133 +84.110.191.75 - - [27/Feb/2008:07:22:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.191.75 - - [27/Feb/2008:07:22:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET / HTTP/1.1" 200 4447 +68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.166.120.178 - - [27/Feb/2008:07:30:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +68.166.120.178 - - [27/Feb/2008:07:30:19 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +217.65.240.234 - - [27/Feb/2008:07:30:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +122.117.168.219 - - [27/Feb/2008:07:30:44 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /about.html HTTP/1.1" 200 7890 +68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +68.166.120.178 - - [27/Feb/2008:07:31:53 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +68.166.120.178 - - [27/Feb/2008:07:34:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +68.166.120.178 - - [27/Feb/2008:07:35:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +189.13.133.202 - - [27/Feb/2008:07:36:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:36:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.166.120.178 - - [27/Feb/2008:07:37:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +61.57.130.42 - - [27/Feb/2008:07:42:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.57.130.42 - - [27/Feb/2008:07:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +61.57.130.42 - - [27/Feb/2008:07:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /robots.txt HTTP/1.0" 200 71 +205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.124.252.210 - - [27/Feb/2008:07:50:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +61.57.130.42 - - [27/Feb/2008:07:53:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.196.43.134 - - [27/Feb/2008:08:05:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.71.101.182 - - [27/Feb/2008:08:06:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:38 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:47 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +194.186.83.193 - - [27/Feb/2008:08:12:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +194.186.83.193 - - [27/Feb/2008:08:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:20:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.207.152.130 - - [27/Feb/2008:08:20:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +58.207.152.130 - - [27/Feb/2008:08:21:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +58.207.152.130 - - [27/Feb/2008:08:21:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.177.245 - - [27/Feb/2008:08:22:09 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.245 - - [27/Feb/2008:08:22:10 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1098 +202.163.114.53 - - [27/Feb/2008:08:23:09 -0600] "GET /cv.html HTTP/1.1" 200 31798 +189.31.124.147 - - [27/Feb/2008:08:27:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.88.162.35 - - [27/Feb/2008:08:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +189.31.124.147 - - [27/Feb/2008:08:27:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.31.124.147 - - [27/Feb/2008:08:27:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +144.204.65.6 - - [27/Feb/2008:08:29:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.31.124.147 - - [27/Feb/2008:08:29:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:29:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.14.110.6 - - [27/Feb/2008:08:29:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.14.110.6 - - [27/Feb/2008:08:29:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:08:31:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:34:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [27/Feb/2008:08:34:55 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.255.240.194 - - [27/Feb/2008:08:36:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +62.255.240.194 - - [27/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +62.255.240.194 - - [27/Feb/2008:08:36:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +60.176.145.165 - - [27/Feb/2008:08:37:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.255.240.194 - - [27/Feb/2008:08:37:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:08:41:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.186.27.193 - - [27/Feb/2008:08:45:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +91.186.27.193 - - [27/Feb/2008:08:45:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.223.13.234 - - [27/Feb/2008:08:46:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +76.223.13.234 - - [27/Feb/2008:08:48:13 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +74.6.25.142 - - [27/Feb/2008:08:48:52 -0600] "GET /python/tutorial/ HTTP/1.0" 403 214 +128.135.212.179 - - [27/Feb/2008:08:49:23 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.212.179 - - [27/Feb/2008:08:51:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.246.7.155 - - [27/Feb/2008:08:58:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.121.236.201 - - [27/Feb/2008:08:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.121.236.201 - - [27/Feb/2008:08:58:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.121.236.201 - - [27/Feb/2008:08:58:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.246.7.155 - - [27/Feb/2008:08:59:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +98.206.164.173 - - [27/Feb/2008:09:04:16 -0600] "GET /dynamic/ HTTP/1.1" 304 - +89.165.73.133 - - [27/Feb/2008:09:04:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.59.141.172 - - [27/Feb/2008:09:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.59.141.172 - - [27/Feb/2008:09:05:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.161.65.194 - - [27/Feb/2008:09:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.124.13.183 - - [27/Feb/2008:09:13:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.43.32.87 - - [27/Feb/2008:09:14:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.165.73.133 - - [27/Feb/2008:09:16:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:09:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.187.74 - - [27/Feb/2008:09:27:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [27/Feb/2008:09:27:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1391 +150.210.155.167 - - [27/Feb/2008:09:29:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.236.90.186 - - [27/Feb/2008:09:30:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.236.90.186 - - [27/Feb/2008:09:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.236.90.186 - - [27/Feb/2008:09:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +74.6.20.147 - - [27/Feb/2008:09:34:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 +192.54.144.229 - - [27/Feb/2008:09:35:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.245.138.101 - - [27/Feb/2008:09:37:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.138.101 - - [27/Feb/2008:09:37:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.138.101 - - [27/Feb/2008:09:37:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.74.100.50 - - [27/Feb/2008:09:38:15 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.122.102.16 - - [27/Feb/2008:09:40:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.122.102.16 - - [27/Feb/2008:09:40:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 19833 +87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +87.122.102.16 - - [27/Feb/2008:09:41:01 -0600] "GET /ply/ply.html HTTP/1.1" 206 90365 +124.161.65.194 - - [27/Feb/2008:09:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.19 - - [27/Feb/2008:09:43:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +124.161.65.194 - - [27/Feb/2008:09:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +155.140.133.62 - - [27/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.24.211.116 - - [27/Feb/2008:09:43:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.24.211.116 - - [27/Feb/2008:09:43:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.24.211.116 - - [27/Feb/2008:09:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +155.140.133.62 - - [27/Feb/2008:09:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.63.137.190 - - [27/Feb/2008:10:01:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 923 +212.63.137.190 - - [27/Feb/2008:10:01:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1009 +212.63.137.190 - - [27/Feb/2008:10:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +141.115.28.2 - - [27/Feb/2008:10:02:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.219.135.118 - - [27/Feb/2008:10:03:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.219.135.118 - - [27/Feb/2008:10:04:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [27/Feb/2008:10:07:34 -0600] "GET /ply/ HTTP/1.0" 304 - +133.9.245.73 - - [27/Feb/2008:10:07:38 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.151.52.19 - - [27/Feb/2008:10:16:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.151.52.19 - - [27/Feb/2008:10:16:07 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +209.159.33.99 - - [27/Feb/2008:10:21:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.159.33.99 - - [27/Feb/2008:10:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +157.157.91.18 - - [27/Feb/2008:10:25:22 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +66.249.65.37 - - [27/Feb/2008:10:29:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.80.231 - - [27/Feb/2008:10:29:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.80.231 - - [27/Feb/2008:10:29:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /ply/README HTTP/1.1" 200 8605 +69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply HTTP/1.1" 301 242 +69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.218.90.173 - - [27/Feb/2008:10:32:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.218.90.173 - - [27/Feb/2008:10:33:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +24.7.210.64 - - [27/Feb/2008:10:40:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [27/Feb/2008:10:40:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.209.249.3 - - [27/Feb/2008:10:41:05 -0600] "GET /python.html HTTP/1.1" 200 18870 +205.209.249.3 - - [27/Feb/2008:10:41:06 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +41.219.193.49 - - [27/Feb/2008:10:44:25 -0600] "GET / HTTP/1.1" 200 4447 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:50:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [27/Feb/2008:10:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:50:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +141.142.240.56 - - [27/Feb/2008:10:52:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.216.13.202 - - [27/Feb/2008:10:53:04 -0600] "GET /ply HTTP/1.1" 301 242 +68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +198.175.55.5 - - [27/Feb/2008:10:54:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +198.175.55.5 - - [27/Feb/2008:10:54:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.175.55.5 - - [27/Feb/2008:10:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.9.243.111 - - [27/Feb/2008:10:56:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +198.175.55.5 - - [27/Feb/2008:10:56:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [27/Feb/2008:10:57:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [27/Feb/2008:10:57:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.53.65.44 - - [27/Feb/2008:11:00:56 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +84.110.205.221 - - [27/Feb/2008:11:02:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.205.221 - - [27/Feb/2008:11:02:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +89.165.73.133 - - [27/Feb/2008:11:04:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.59.123.114 - - [27/Feb/2008:11:06:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.59.123.114 - - [27/Feb/2008:11:07:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.59.123.114 - - [27/Feb/2008:11:07:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +89.165.73.133 - - [27/Feb/2008:11:09:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:09:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:09:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.214.175.76 - - [27/Feb/2008:11:09:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.196.80.231 - - [27/Feb/2008:11:11:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.221.26.30 - - [27/Feb/2008:11:16:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:11:18:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +201.236.226.90 - - [27/Feb/2008:11:19:12 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +128.196.80.231 - - [27/Feb/2008:11:22:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 +200.55.140.181 - - [27/Feb/2008:11:27:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.55.140.181 - - [27/Feb/2008:11:27:23 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.28.45 - - [27/Feb/2008:11:33:12 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 +128.196.80.231 - - [27/Feb/2008:11:33:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +83.24.38.176 - - [27/Feb/2008:11:37:57 -0600] "GET /ply/ HTTP/1.1" 304 - +83.24.38.176 - - [27/Feb/2008:11:38:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.24.38.176 - - [27/Feb/2008:11:43:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.38.176 - - [27/Feb/2008:11:43:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +86.141.0.114 - - [27/Feb/2008:11:51:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:51:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:52:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +24.1.159.241 - - [27/Feb/2008:11:57:55 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +87.221.119.235 - - [27/Feb/2008:11:58:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +87.221.119.235 - - [27/Feb/2008:11:58:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.221.119.235 - - [27/Feb/2008:11:58:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.199.163.61 - - [27/Feb/2008:12:06:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /python.html HTTP/1.1" 200 18870 +80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.191.172.9 - - [27/Feb/2008:12:14:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +204.246.129.196 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +74.6.25.20 - - [27/Feb/2008:12:21:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.25.22 - - [27/Feb/2008:12:21:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 +74.6.26.235 - - [27/Feb/2008:12:21:48 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1582 +128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.118 - - [27/Feb/2008:12:35:24 -0600] "GET /photos/wind/pages/IMG_1253.htm HTTP/1.1" 404 133 +128.196.205.39 - - [27/Feb/2008:12:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +209.17.146.129 - - [27/Feb/2008:12:44:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "HEAD /ply/ HTTP/1.0" 200 0 +84.110.209.164 - - [27/Feb/2008:12:47:47 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.209.164 - - [27/Feb/2008:12:47:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1032 +67.228.97.106 - - [27/Feb/2008:12:48:23 -0600] "HEAD /ply/example.html HTTP/1.0" 304 - +76.185.24.146 - - [27/Feb/2008:12:49:54 -0600] "GET /python.html HTTP/1.1" 200 18870 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.185.24.146 - - [27/Feb/2008:12:52:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +76.185.24.146 - - [27/Feb/2008:12:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.162.120.220 - - [27/Feb/2008:12:56:06 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +76.185.24.146 - - [27/Feb/2008:12:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.41 - - [27/Feb/2008:12:59:13 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1336 +76.185.24.146 - - [27/Feb/2008:12:59:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +134.173.88.178 - - [27/Feb/2008:13:01:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.186.48.152 - - [27/Feb/2008:13:02:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [27/Feb/2008:13:02:59 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 +76.185.24.146 - - [27/Feb/2008:13:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.5.113.131 - - [27/Feb/2008:13:13:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +74.6.26.145 - - [27/Feb/2008:13:14:05 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1322 +134.173.88.178 - - [27/Feb/2008:13:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.51.43.161 - - [27/Feb/2008:13:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +144.51.43.161 - - [27/Feb/2008:13:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +144.51.43.161 - - [27/Feb/2008:13:24:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +144.51.43.161 - - [27/Feb/2008:13:24:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +144.51.43.161 - - [27/Feb/2008:13:25:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +195.214.232.10 - - [27/Feb/2008:13:32:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.214.232.10 - - [27/Feb/2008:13:32:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.48.8.237 - - [27/Feb/2008:13:32:59 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.123 - - [27/Feb/2008:13:41:55 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +88.191.19.81 - - [27/Feb/2008:13:42:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.198.239.27 - - [27/Feb/2008:13:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.179.69 - - [27/Feb/2008:13:44:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.173.88.178 - - [27/Feb/2008:13:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.191.255.98 - - [27/Feb/2008:13:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.191.255.98 - - [27/Feb/2008:13:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +68.191.255.98 - - [27/Feb/2008:13:47:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.191.255.98 - - [27/Feb/2008:13:47:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +76.171.199.78 - - [27/Feb/2008:13:48:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.171.199.78 - - [27/Feb/2008:13:48:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.171.199.78 - - [27/Feb/2008:13:49:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +12.107.176.254 - - [27/Feb/2008:13:50:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.218.75.11 - - [27/Feb/2008:13:59:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +70.218.75.11 - - [27/Feb/2008:13:59:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.195.58.158 - - [27/Feb/2008:14:00:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +64.234.244.162 - - [27/Feb/2008:14:01:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.171.199.78 - - [27/Feb/2008:14:02:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET / HTTP/1.1" 200 4447 +128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /python.html HTTP/1.1" 200 18870 +128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +84.110.138.116 - - [27/Feb/2008:14:08:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.138.116 - - [27/Feb/2008:14:08:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 +171.66.35.216 - - [27/Feb/2008:14:12:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +171.66.35.216 - - [27/Feb/2008:14:12:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +24.15.187.198 - - [27/Feb/2008:14:13:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [27/Feb/2008:14:14:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [27/Feb/2008:14:14:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +129.55.200.20 - - [27/Feb/2008:14:16:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +128.143.117.220 - - [27/Feb/2008:14:16:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.117.220 - - [27/Feb/2008:14:16:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 145565 +86.206.17.195 - - [27/Feb/2008:14:19:25 -0600] "GET / HTTP/1.1" 200 4447 +86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.206.17.195 - - [27/Feb/2008:14:19:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 +86.206.17.195 - - [27/Feb/2008:14:19:33 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +199.111.200.69 - - [27/Feb/2008:14:26:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.44.110 - - [27/Feb/2008:14:26:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [27/Feb/2008:14:26:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.31.151 - - [27/Feb/2008:14:28:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +24.15.187.198 - - [27/Feb/2008:14:30:33 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [27/Feb/2008:14:30:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.14.206.105 - - [27/Feb/2008:14:36:41 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +88.165.126.62 - - [27/Feb/2008:14:39:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.165.126.62 - - [27/Feb/2008:14:39:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.165.126.62 - - [27/Feb/2008:14:39:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.6.151.80 - - [27/Feb/2008:14:40:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +24.2.76.175 - - [27/Feb/2008:14:45:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.2.76.175 - - [27/Feb/2008:14:45:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.15.187.198 - - [27/Feb/2008:14:54:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +84.134.8.241 - - [27/Feb/2008:14:56:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.134.8.241 - - [27/Feb/2008:14:56:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:56:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:57:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.15.187.198 - - [27/Feb/2008:15:01:44 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +220.239.245.127 - - [27/Feb/2008:15:01:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.239.245.127 - - [27/Feb/2008:15:01:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.239.245.127 - - [27/Feb/2008:15:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [27/Feb/2008:15:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.15.187.198 - - [27/Feb/2008:15:06:22 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.67.224.55 - - [27/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +88.67.224.55 - - [27/Feb/2008:15:13:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.124 - - [27/Feb/2008:15:15:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:15:15:57 -0600] "GET / HTTP/1.1" 200 4447 +64.0.160.210 - - [27/Feb/2008:15:17:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.0.160.210 - - [27/Feb/2008:15:17:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply HTTP/1.1" 301 242 +64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.0.160.210 - - [27/Feb/2008:15:18:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.0.160.210 - - [27/Feb/2008:15:18:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +200.11.208.122 - - [27/Feb/2008:15:20:01 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +200.11.208.122 - - [27/Feb/2008:15:20:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.13 - - [27/Feb/2008:15:22:32 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +77.91.224.3 - - [27/Feb/2008:15:22:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.3 - - [27/Feb/2008:15:22:58 -0600] "GET /swill/ HTTP/1.1" 200 3786 +77.91.224.13 - - [27/Feb/2008:15:24:51 -0600] "GET /publications.html HTTP/1.1" 200 7758 +89.168.18.6 - - [27/Feb/2008:15:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +89.168.18.6 - - [27/Feb/2008:15:26:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:26:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [27/Feb/2008:15:26:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 +89.168.18.6 - - [27/Feb/2008:15:26:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +89.168.18.6 - - [27/Feb/2008:15:27:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +89.168.18.6 - - [27/Feb/2008:15:28:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +77.91.224.13 - - [27/Feb/2008:15:28:03 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +89.168.18.6 - - [27/Feb/2008:15:28:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +89.168.18.6 - - [27/Feb/2008:15:28:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +89.168.18.6 - - [27/Feb/2008:15:28:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +77.91.224.3 - - [27/Feb/2008:15:28:35 -0600] "GET /diet.html HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:28:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +77.91.224.3 - - [27/Feb/2008:15:29:27 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +89.168.18.6 - - [27/Feb/2008:15:29:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +89.168.18.6 - - [27/Feb/2008:15:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +89.168.18.6 - - [27/Feb/2008:15:30:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +89.168.18.6 - - [27/Feb/2008:15:30:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +89.168.18.6 - - [27/Feb/2008:15:30:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +89.168.18.6 - - [27/Feb/2008:15:30:39 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +77.91.224.13 - - [27/Feb/2008:15:31:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +77.91.224.3 - - [27/Feb/2008:15:31:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +77.91.224.3 - - [27/Feb/2008:15:32:37 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +77.91.224.13 - - [27/Feb/2008:15:34:18 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +77.91.224.3 - - [27/Feb/2008:15:34:58 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +66.249.65.37 - - [27/Feb/2008:15:36:30 -0600] "GET / HTTP/1.1" 304 - +77.91.224.13 - - [27/Feb/2008:15:36:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +77.91.224.3 - - [27/Feb/2008:15:38:12 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET / HTTP/1.1" 200 4447 +66.249.65.37 - - [27/Feb/2008:15:42:08 -0600] "GET /python.html HTTP/1.1" 304 - +66.249.65.37 - - [27/Feb/2008:15:43:17 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +209.198.142.114 - - [27/Feb/2008:15:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.198.142.114 - - [27/Feb/2008:15:44:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.202.49.172 - - [27/Feb/2008:15:46:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /publications.html HTTP/1.1" 200 7758 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +209.198.142.114 - - [27/Feb/2008:15:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +209.198.142.114 - - [27/Feb/2008:15:48:30 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +66.249.65.37 - - [27/Feb/2008:15:48:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +209.198.142.114 - - [27/Feb/2008:15:50:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +209.198.142.114 - - [27/Feb/2008:15:50:18 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +80.229.70.194 - - [27/Feb/2008:15:51:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.70.194 - - [27/Feb/2008:15:51:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [27/Feb/2008:15:51:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.202.49.172 - - [27/Feb/2008:15:52:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 +65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /photos/wind/ThumbnailFrame.htm HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:15:54:20 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.67.242.79 - - [27/Feb/2008:15:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.221 - - [27/Feb/2008:15:58:32 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +87.194.206.16 - - [27/Feb/2008:15:58:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.115 - - [27/Feb/2008:15:59:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 +66.249.65.37 - - [27/Feb/2008:15:59:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 +87.194.206.16 - - [27/Feb/2008:15:59:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [27/Feb/2008:16:01:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:16:01:43 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 304 - +74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.194.206.16 - - [27/Feb/2008:16:03:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.92.109.170 - - [27/Feb/2008:16:04:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +84.92.109.170 - - [27/Feb/2008:16:04:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.92.109.170 - - [27/Feb/2008:16:04:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +84.92.109.170 - - [27/Feb/2008:16:04:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +84.92.109.170 - - [27/Feb/2008:16:04:47 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +66.249.65.37 - - [27/Feb/2008:16:05:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.204.154 - - [27/Feb/2008:16:11:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.204.154 - - [27/Feb/2008:16:11:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.160.75.249 - - [27/Feb/2008:16:12:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.160.75.249 - - [27/Feb/2008:16:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +216.160.75.249 - - [27/Feb/2008:16:12:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +216.160.75.249 - - [27/Feb/2008:16:13:10 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +216.160.75.249 - - [27/Feb/2008:16:13:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +71.201.41.248 - - [27/Feb/2008:16:16:47 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +71.201.41.248 - - [27/Feb/2008:16:16:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [27/Feb/2008:16:16:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.125.239 - - [27/Feb/2008:16:17:53 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +24.1.159.241 - - [27/Feb/2008:16:23:55 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:16:24:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +89.168.18.6 - - [27/Feb/2008:16:24:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.168.18.6 - - [27/Feb/2008:16:24:50 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +89.168.18.6 - - [27/Feb/2008:16:25:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +89.168.18.6 - - [27/Feb/2008:16:25:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +89.168.18.6 - - [27/Feb/2008:16:25:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +65.55.212.77 - - [27/Feb/2008:16:25:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +84.75.247.28 - - [27/Feb/2008:16:27:33 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 15592 +84.75.247.28 - - [27/Feb/2008:16:27:44 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 +99.226.226.178 - - [27/Feb/2008:16:31:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:16:31:56 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 304 - +74.6.26.159 - - [27/Feb/2008:16:32:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE061.HTM HTTP/1.0" 304 - +128.221.197.20 - - [27/Feb/2008:16:34:07 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +87.205.225.143 - - [27/Feb/2008:16:42:46 -0600] "GET /ply/ HTTP/1.0" 304 - +24.99.94.177 - - [27/Feb/2008:16:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.194.136.18 - - [27/Feb/2008:16:45:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.248.3 - - [27/Feb/2008:16:46:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [27/Feb/2008:16:47:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.232.113.194 - - [27/Feb/2008:16:49:35 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +202.106.212.226 - - [27/Feb/2008:16:49:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +66.249.65.37 - - [27/Feb/2008:16:49:54 -0600] "GET /index.html HTTP/1.1" 304 - +74.6.7.110 - - [27/Feb/2008:16:58:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 +199.111.224.90 - - [27/Feb/2008:16:58:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.201 - - [27/Feb/2008:17:00:39 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.19.201 - - [27/Feb/2008:17:00:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.59.157 - - [27/Feb/2008:17:02:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1036 +134.173.59.157 - - [27/Feb/2008:17:02:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +134.173.59.157 - - [27/Feb/2008:17:11:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.173.59.157 - - [27/Feb/2008:17:11:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +134.173.59.157 - - [27/Feb/2008:17:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:12:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:12:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.10.16.193 - - [27/Feb/2008:17:17:30 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [27/Feb/2008:17:17:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:17:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:19:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:17:20:58 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +206.51.237.114 - - [27/Feb/2008:17:22:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +203.113.115.20 - - [27/Feb/2008:17:22:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [27/Feb/2008:17:22:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.67.242.79 - - [27/Feb/2008:17:23:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +134.173.59.157 - - [27/Feb/2008:17:25:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +210.9.32.205 - - [27/Feb/2008:17:25:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.9.32.205 - - [27/Feb/2008:17:25:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.9.32.205 - - [27/Feb/2008:17:25:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [27/Feb/2008:17:26:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.141 - - [27/Feb/2008:17:27:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.173.59.157 - - [27/Feb/2008:17:28:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1858 +134.173.59.157 - - [27/Feb/2008:17:28:35 -0600] "GET /cgi-bin/wiki.pl?InstallationProblems HTTP/1.1" 200 4209 +24.10.16.193 - - [27/Feb/2008:17:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.59.157 - - [27/Feb/2008:17:29:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 971 +134.173.59.157 - - [27/Feb/2008:17:29:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +199.111.224.90 - - [27/Feb/2008:17:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:31:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:35:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [27/Feb/2008:17:40:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.67.242.79 - - [27/Feb/2008:17:42:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +213.67.242.79 - - [27/Feb/2008:17:42:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +148.87.1.171 - - [27/Feb/2008:17:44:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +148.87.1.171 - - [27/Feb/2008:17:44:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +212.90.208.194 - - [27/Feb/2008:17:44:33 -0600] "GET /cv.html HTTP/1.1" 200 31798 +24.10.16.193 - - [27/Feb/2008:17:47:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:47:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +89.224.135.187 - - [27/Feb/2008:17:53:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +81.103.63.40 - - [27/Feb/2008:17:54:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +68.42.70.206 - - [27/Feb/2008:17:56:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:18:00:28 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [27/Feb/2008:18:01:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.180.132.213 - - [27/Feb/2008:18:09:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [27/Feb/2008:18:13:05 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.146.214.212 - - [27/Feb/2008:18:13:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +86.157.119.197 - - [27/Feb/2008:18:16:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.223 - - [27/Feb/2008:18:17:08 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 +67.195.58.174 - - [27/Feb/2008:18:23:38 -0600] "GET /ply/ HTTP/1.0" 304 - +140.180.132.213 - - [27/Feb/2008:18:24:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.69.160.150 - - [27/Feb/2008:18:25:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +24.69.160.150 - - [27/Feb/2008:18:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +24.69.160.150 - - [27/Feb/2008:18:27:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +24.69.160.150 - - [27/Feb/2008:18:27:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMissingHeaderFiles HTTP/1.1" 200 3193 +24.69.160.150 - - [27/Feb/2008:18:27:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +24.69.160.150 - - [27/Feb/2008:18:27:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDevelopmentMachines HTTP/1.1" 200 1781 +24.69.160.150 - - [27/Feb/2008:18:27:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +24.69.160.150 - - [27/Feb/2008:18:27:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +24.69.160.150 - - [27/Feb/2008:18:28:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +24.69.160.150 - - [27/Feb/2008:18:28:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +24.69.160.150 - - [27/Feb/2008:18:28:17 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 +24.69.160.150 - - [27/Feb/2008:18:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +24.69.160.150 - - [27/Feb/2008:18:28:37 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +24.69.160.150 - - [27/Feb/2008:18:28:42 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 +24.69.160.150 - - [27/Feb/2008:18:28:45 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +24.69.160.150 - - [27/Feb/2008:18:29:08 -0600] "GET /cgi-bin/wiki.pl?PrettyXmlSwig HTTP/1.1" 200 1495 +24.69.160.150 - - [27/Feb/2008:18:29:24 -0600] "GET /cgi-bin/wiki.pl?Unit_Tests_With_SWIG HTTP/1.1" 200 2103 +66.146.214.212 - - [27/Feb/2008:18:31:34 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +74.6.26.203 - - [27/Feb/2008:18:33:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 +67.195.58.158 - - [27/Feb/2008:18:35:27 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +67.195.58.158 - - [27/Feb/2008:18:35:28 -0600] "GET /ply/README HTTP/1.0" 304 - +199.111.224.90 - - [27/Feb/2008:18:41:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:43:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:43:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.192.172.49 - - [27/Feb/2008:18:52:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.192.172.49 - - [27/Feb/2008:18:54:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.57.91.136 - - [27/Feb/2008:18:54:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +85.192.172.49 - - [27/Feb/2008:18:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.192.172.49 - - [27/Feb/2008:18:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [27/Feb/2008:18:55:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +71.57.91.136 - - [27/Feb/2008:18:55:18 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +71.57.91.136 - - [27/Feb/2008:18:55:38 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +85.192.172.49 - - [27/Feb/2008:18:55:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +85.192.172.49 - - [27/Feb/2008:18:55:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +207.47.10.211 - - [27/Feb/2008:18:59:09 -0600] "GET /python.html HTTP/1.1" 200 18870 +207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.145 - - [27/Feb/2008:18:59:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +199.111.224.90 - - [27/Feb/2008:19:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.110.86.80 - - [27/Feb/2008:19:04:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET / HTTP/1.0" 200 4447 +62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET /training.html HTTP/1.0" 200 6154 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /software.html HTTP/1.0" 200 3163 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /about.html HTTP/1.0" 200 7890 +62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /python.html HTTP/1.0" 200 18870 +62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /index.html HTTP/1.0" 200 4447 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/README HTTP/1.0" 200 8605 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /cv.html HTTP/1.0" 200 31798 +62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.0" 200 158 +62.149.227.70 - - [27/Feb/2008:19:13:14 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +69.91.149.168 - - [27/Feb/2008:19:14:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.149.168 - - [27/Feb/2008:19:15:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.205.204 - - [27/Feb/2008:19:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:19:21:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.108 - - [27/Feb/2008:19:22:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +67.195.34.114 - - [27/Feb/2008:19:23:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +24.127.118.125 - - [27/Feb/2008:19:23:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:24:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:29:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /photos/u505/pages/IMG_1522.htm HTTP/1.0" 404 133 +67.195.34.111 - - [27/Feb/2008:19:31:15 -0600] "GET /ply HTTP/1.0" 301 230 +199.111.205.204 - - [27/Feb/2008:19:31:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.111 - - [27/Feb/2008:19:31:19 -0600] "GET /ply/ HTTP/1.0" 200 8018 +24.127.118.125 - - [27/Feb/2008:19:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.92.161.138 - - [27/Feb/2008:19:37:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 +193.252.149.15 - - [27/Feb/2008:19:38:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:19:38:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +201.50.187.62 - - [27/Feb/2008:19:39:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.97 - - [27/Feb/2008:19:40:04 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [27/Feb/2008:19:40:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 +24.127.118.125 - - [27/Feb/2008:19:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:43:58 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [27/Feb/2008:19:44:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:44:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +199.111.224.90 - - [27/Feb/2008:19:44:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:19:44:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:44:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [27/Feb/2008:19:50:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.180.198.130 - - [27/Feb/2008:19:59:26 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 +199.111.205.204 - - [27/Feb/2008:20:04:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.124.35.196 - - [27/Feb/2008:20:05:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +32.145.0.255 - - [27/Feb/2008:20:06:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +32.145.0.255 - - [27/Feb/2008:20:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:07:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:09:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +66.29.115.55 - - [27/Feb/2008:20:14:47 -0600] "GET / HTTP/1.1" 200 4447 +65.55.235.161 - - [27/Feb/2008:20:22:00 -0600] "GET /robots.txt HTTP/1.0" 200 71 +122.208.5.10 - - [27/Feb/2008:20:22:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +122.208.5.10 - - [27/Feb/2008:20:22:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.208.5.10 - - [27/Feb/2008:20:22:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +122.208.5.10 - - [27/Feb/2008:20:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.38.140.124 - - [27/Feb/2008:20:23:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.25.105.176 - - [27/Feb/2008:20:24:40 -0600] "GET / HTTP/1.1" 200 4447 +72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:29:46 -0600] "GET /about.html HTTP/1.1" 200 7890 +65.55.232.14 - - [27/Feb/2008:20:32:11 -0600] "GET /training.html HTTP/1.1" 200 6154 +199.111.205.204 - - [27/Feb/2008:20:32:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET / HTTP/1.0" 200 4447 +199.111.205.204 - - [27/Feb/2008:20:37:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:38:56 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +65.55.232.14 - - [27/Feb/2008:20:38:58 -0600] "GET /index.html HTTP/1.1" 200 4447 +65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /software.html HTTP/1.1" 200 3163 +65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 +199.111.205.204 - - [27/Feb/2008:20:39:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.190.54 - - [27/Feb/2008:20:41:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +199.111.205.204 - - [27/Feb/2008:20:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:20:43:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.204.154 - - [27/Feb/2008:20:44:26 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.205.204 - - [27/Feb/2008:20:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.158 - - [27/Feb/2008:20:53:53 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +70.190.236.166 - - [27/Feb/2008:20:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.54.165.36 - - [27/Feb/2008:21:03:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 +69.213.243.113 - - [27/Feb/2008:21:04:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [27/Feb/2008:21:04:25 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +69.213.243.113 - - [27/Feb/2008:21:04:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +69.213.243.113 - - [27/Feb/2008:21:04:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +199.111.205.204 - - [27/Feb/2008:21:05:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.176.224.242 - - [27/Feb/2008:21:05:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 +199.111.205.204 - - [27/Feb/2008:21:11:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [27/Feb/2008:21:11:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply HTTP/1.1" 301 242 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:32 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET / HTTP/1.1" 200 4447 +132.206.52.168 - - [27/Feb/2008:21:13:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.1.247.118 - - [27/Feb/2008:21:13:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - +38.98.120.84 - - [27/Feb/2008:21:14:20 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:21 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:22 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:14:25 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:27 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:28 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [27/Feb/2008:21:14:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:34 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [27/Feb/2008:21:14:35 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:36 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:39 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [27/Feb/2008:21:14:41 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:47 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:48 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:14:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +38.98.120.84 - - [27/Feb/2008:21:14:50 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:51 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:52 -0600] "GET /swill/software.html HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:21:14:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:53 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:56 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:57 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /diet.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [27/Feb/2008:21:15:03 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:04 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [27/Feb/2008:21:15:05 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:15:06 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:07 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:08 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:09 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:10 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:11 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:12 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:13 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [27/Feb/2008:21:15:14 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:15 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:16 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:17 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:19 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:20 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:21 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:23 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [27/Feb/2008:21:15:24 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:25 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:27 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:28 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:29 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [27/Feb/2008:21:15:30 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:32 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:33 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:34 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:36 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:37 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [27/Feb/2008:21:15:38 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [27/Feb/2008:21:15:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:15:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:46 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:47 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:48 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:15:50 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:51 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [27/Feb/2008:21:15:52 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:53 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [27/Feb/2008:21:15:54 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:55 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:15:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 +38.98.120.84 - - [27/Feb/2008:21:15:57 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:58 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:00 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [27/Feb/2008:21:16:01 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:16:02 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [27/Feb/2008:21:16:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:04 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [27/Feb/2008:21:16:05 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:06 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:07 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:08 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [27/Feb/2008:21:16:09 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:16:11 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [27/Feb/2008:21:16:12 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:13 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 +38.98.120.84 - - [27/Feb/2008:21:16:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [27/Feb/2008:21:16:15 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +38.98.120.84 - - [27/Feb/2008:21:16:16 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [27/Feb/2008:21:16:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:18 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:19 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [27/Feb/2008:21:16:20 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:21 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:23 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:24 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:16:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:26 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:16:28 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:29 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [27/Feb/2008:21:16:34 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 +38.98.120.84 - - [27/Feb/2008:21:16:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [27/Feb/2008:21:16:36 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [27/Feb/2008:21:16:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:38 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [27/Feb/2008:21:16:46 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:16:47 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [27/Feb/2008:21:16:48 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [27/Feb/2008:21:16:49 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:50 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:51 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:54 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:55 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 +38.98.120.84 - - [27/Feb/2008:21:16:56 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:57 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:58 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:17:29 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 119844 +199.111.205.204 - - [27/Feb/2008:21:20:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [27/Feb/2008:21:27:16 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +76.206.238.222 - - [27/Feb/2008:21:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 +217.172.44.82 - - [27/Feb/2008:21:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +65.55.232.14 - - [27/Feb/2008:21:30:38 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +72.87.11.163 - - [27/Feb/2008:21:31:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.87.11.163 - - [27/Feb/2008:21:31:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.232.14 - - [27/Feb/2008:21:37:45 -0600] "GET /ply/README HTTP/1.1" 304 - +76.206.238.222 - - [27/Feb/2008:21:38:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 79526 +76.206.238.222 - - [27/Feb/2008:21:38:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 135312 +128.135.139.146 - - [27/Feb/2008:21:40:21 -0600] "GET /dynamic/ HTTP/1.1" 304 - +65.55.232.14 - - [27/Feb/2008:21:41:42 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +24.10.16.193 - - [27/Feb/2008:21:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:42:07 -0600] "GET /cv.html HTTP/1.1" 200 31798 +65.55.232.14 - - [27/Feb/2008:21:42:09 -0600] "GET /diet.html HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:42:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:43:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +65.55.232.14 - - [27/Feb/2008:21:46:59 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +65.55.232.14 - - [27/Feb/2008:21:47:03 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 +24.10.16.193 - - [27/Feb/2008:21:47:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:47:21 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 +24.10.16.193 - - [27/Feb/2008:21:48:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.15 - - [27/Feb/2008:21:49:23 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.15 - - [27/Feb/2008:21:49:27 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 3246437 +24.10.16.193 - - [27/Feb/2008:21:51:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:21:52:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:53:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:54:05 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +24.10.16.193 - - [27/Feb/2008:21:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:56:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:00:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:01:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.194.239 - - [27/Feb/2008:22:02:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.194.239 - - [27/Feb/2008:22:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.194.239 - - [27/Feb/2008:22:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.54.165.36 - - [27/Feb/2008:22:06:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +65.55.232.14 - - [27/Feb/2008:22:07:12 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /publications.html HTTP/1.1" 200 7758 +65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +65.55.232.14 - - [27/Feb/2008:22:07:22 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +65.55.232.14 - - [27/Feb/2008:22:07:24 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +24.10.16.193 - - [27/Feb/2008:22:09:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:22:09:50 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 200 514533 +24.10.16.193 - - [27/Feb/2008:22:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:13:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:14:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:22:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.57.245.11 - - [27/Feb/2008:22:17:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.57.245.11 - - [27/Feb/2008:22:19:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.57.245.11 - - [27/Feb/2008:22:19:19 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET / HTTP/1.1" 200 4447 +201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET /ply HTTP/1.1" 301 242 +201.78.136.222 - - [27/Feb/2008:22:21:38 -0600] "GET /about.html HTTP/1.1" 200 7890 +199.111.205.204 - - [27/Feb/2008:22:22:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:24:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.57.245.11 - - [27/Feb/2008:22:25:47 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +199.111.205.204 - - [27/Feb/2008:22:26:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:27:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:28:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:28:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.10.16.193 - - [27/Feb/2008:22:34:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [27/Feb/2008:22:38:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [27/Feb/2008:22:38:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.170 - - [27/Feb/2008:22:41:47 -0600] "GET / HTTP/1.0" 304 - +67.195.58.186 - - [27/Feb/2008:22:43:05 -0600] "GET /about.html HTTP/1.0" 200 7890 +128.143.255.129 - - [27/Feb/2008:22:48:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:22:55:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +190.128.108.147 - - [27/Feb/2008:23:07:42 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +190.128.108.147 - - [27/Feb/2008:23:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.128.108.147 - - [27/Feb/2008:23:07:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.128.108.147 - - [27/Feb/2008:23:07:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.210.132.13 - - [27/Feb/2008:23:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:23:23:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.72.178.39 - - [27/Feb/2008:23:23:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +75.72.178.39 - - [27/Feb/2008:23:23:54 -0600] "GET /ply/README HTTP/1.1" 200 8605 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:23:30:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +24.10.16.193 - - [27/Feb/2008:23:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.107.0.101 - - [27/Feb/2008:23:41:48 -0600] "GET /photos/u505/pages/IMG_1543.htm HTTP/1.1" 404 133 +65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - +65.55.208.123 - - [27/Feb/2008:23:45:22 -0600] "GET /robots.txt HTTP/1.1" 200 71 +199.111.194.239 - - [27/Feb/2008:23:46:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.29.115.55 - - [27/Feb/2008:23:48:49 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.232.113.62 - - [27/Feb/2008:23:49:57 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +201.63.117.142 - - [27/Feb/2008:23:50:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +217.172.56.49 - - [27/Feb/2008:23:50:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +195.229.242.154 - - [27/Feb/2008:23:50:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +89.122.29.76 - - [27/Feb/2008:23:54:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.10.16.193 - - [27/Feb/2008:23:59:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +206.51.226.87 - - [28/Feb/2008:00:13:22 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +213.209.210.70 - - [28/Feb/2008:00:13:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.247.11.155 - - [28/Feb/2008:00:13:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +72.236.184.249 - - [28/Feb/2008:00:13:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /photos/wind/pages/IMG_1282.htm HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:13:48 -0600] "GET /photos/wind/pages/IMG_1329.htm HTTP/1.1" 404 133 +213.145.165.82 - - [28/Feb/2008:00:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.233.181.234 - - [28/Feb/2008:00:20:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:34 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:00:20:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET / HTTP/1.1" 200 4447 +67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.186.98.20 - - [28/Feb/2008:00:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:00:29:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +67.186.98.20 - - [28/Feb/2008:00:29:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:00:31:59 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:37:47 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:37:48 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:51:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:52:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:52:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +195.3.254.138 - - [28/Feb/2008:00:52:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.3.254.138 - - [28/Feb/2008:00:52:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:00:55:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +195.3.254.138 - - [28/Feb/2008:00:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:57:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +67.186.98.20 - - [28/Feb/2008:00:58:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:00:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:58:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +67.186.98.20 - - [28/Feb/2008:00:58:32 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +217.196.43.134 - - [28/Feb/2008:01:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.89.249.77 - - [28/Feb/2008:01:09:28 -0600] "GET /cv.html HTTP/1.1" 200 31798 +84.89.249.77 - - [28/Feb/2008:01:09:37 -0600] "GET / HTTP/1.1" 200 4447 +84.89.249.77 - - [28/Feb/2008:01:09:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +84.89.249.77 - - [28/Feb/2008:01:12:22 -0600] "GET /software.html HTTP/1.1" 200 3163 +65.55.208.118 - - [28/Feb/2008:01:12:45 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - +84.89.249.77 - - [28/Feb/2008:01:12:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 +84.89.249.77 - - [28/Feb/2008:01:12:56 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +84.89.249.77 - - [28/Feb/2008:01:13:21 -0600] "GET /about.html HTTP/1.1" 200 7890 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +84.89.249.77 - - [28/Feb/2008:01:13:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +72.163.216.217 - - [28/Feb/2008:01:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.112 - - [28/Feb/2008:01:18:35 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +65.55.104.13 - - [28/Feb/2008:01:23:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.123.80.214 - - [28/Feb/2008:01:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.123.80.214 - - [28/Feb/2008:01:38:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +67.123.80.214 - - [28/Feb/2008:01:38:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.123.80.214 - - [28/Feb/2008:01:41:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +194.186.83.193 - - [28/Feb/2008:01:42:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 56145 +195.3.254.138 - - [28/Feb/2008:01:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /ply/README HTTP/1.0" 200 8605 +24.223.228.170 - - [28/Feb/2008:01:59:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.223.228.170 - - [28/Feb/2008:01:59:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.223.228.170 - - [28/Feb/2008:01:59:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.232.58 - - [28/Feb/2008:02:02:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:02:02:49 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +61.135.166.102 - - [28/Feb/2008:02:04:34 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [28/Feb/2008:02:07:15 -0600] "GET / HTTP/1.1" 200 4447 +67.195.58.174 - - [28/Feb/2008:02:26:24 -0600] "GET /ply/ HTTP/1.0" 304 - +65.214.45.129 - - [28/Feb/2008:02:26:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 +123.240.74.156 - - [28/Feb/2008:02:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +123.240.74.156 - - [28/Feb/2008:02:30:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +123.240.74.156 - - [28/Feb/2008:02:30:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:32 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +62.161.167.222 - - [28/Feb/2008:02:41:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.161.167.222 - - [28/Feb/2008:02:42:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:49:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:49:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.223.228.170 - - [28/Feb/2008:02:50:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.136.165.22 - - [28/Feb/2008:02:53:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.136.165.22 - - [28/Feb/2008:02:53:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.136.165.22 - - [28/Feb/2008:02:53:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +62.161.167.222 - - [28/Feb/2008:03:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.9.228.166 - - [28/Feb/2008:03:06:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 +209.9.228.174 - - [28/Feb/2008:03:06:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +62.161.167.222 - - [28/Feb/2008:03:08:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [28/Feb/2008:03:08:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.28.223 - - [28/Feb/2008:03:08:49 -0600] "GET /python/python.html HTTP/1.0" 404 133 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET / HTTP/1.1" 200 4447 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +203.101.103.2 - - [28/Feb/2008:03:09:25 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.246.173.21 - - [28/Feb/2008:03:11:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +65.55.208.118 - - [28/Feb/2008:03:12:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 +83.12.228.78 - - [28/Feb/2008:03:14:42 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 +139.149.31.231 - - [28/Feb/2008:03:15:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.149.31.231 - - [28/Feb/2008:03:15:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.3.134.34 - - [28/Feb/2008:03:19:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12707 +218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.92.181.114 - - [28/Feb/2008:03:23:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.92.181.114 - - [28/Feb/2008:03:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.92.181.114 - - [28/Feb/2008:03:23:07 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +62.161.167.222 - - [28/Feb/2008:03:27:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.202.49.172 - - [28/Feb/2008:03:30:36 -0600] "GET /robots.txt HTTP/1.1" 200 71 +218.107.55.253 - - [28/Feb/2008:03:30:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.202.49.172 - - [28/Feb/2008:03:31:12 -0600] "GET /ply HTTP/1.1" 301 242 +67.202.49.172 - - [28/Feb/2008:03:31:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.107.55.253 - - [28/Feb/2008:03:31:36 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +83.206.220.147 - - [28/Feb/2008:03:40:59 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.0" 200 0 +83.206.220.147 - - [28/Feb/2008:03:41:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +82.127.117.160 - - [28/Feb/2008:03:43:59 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +134.173.56.81 - - [28/Feb/2008:03:46:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.145.209.82 - - [28/Feb/2008:03:47:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 +89.145.209.82 - - [28/Feb/2008:03:47:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +134.173.56.81 - - [28/Feb/2008:03:48:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.56.81 - - [28/Feb/2008:03:48:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:03:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.145.209.82 - - [28/Feb/2008:03:52:02 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.252.149.16 - - [28/Feb/2008:03:53:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [28/Feb/2008:03:53:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +66.232.113.194 - - [28/Feb/2008:03:54:54 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2755 +72.236.184.249 - - [28/Feb/2008:03:54:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.222.157.76 - - [28/Feb/2008:03:54:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply HTTP/1.1" 301 242 +86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.25.206.10 - - [28/Feb/2008:03:57:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.138.83.15 - - [28/Feb/2008:04:06:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /photos/wind/pages/IMG_1258.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:04:06:59 -0600] "GET /photos/wind/pages/IMG_1330.htm HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:04:07:37 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.215.100.68 - - [28/Feb/2008:04:07:43 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +98.215.100.68 - - [28/Feb/2008:04:08:07 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +67.195.58.158 - - [28/Feb/2008:04:13:19 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +198.54.202.210 - - [28/Feb/2008:04:13:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [28/Feb/2008:04:22:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +206.51.237.114 - - [28/Feb/2008:04:24:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +189.42.79.133 - - [28/Feb/2008:04:24:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.228.123.66 - - [28/Feb/2008:04:24:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.145.209.82 - - [28/Feb/2008:04:24:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /photos/wind/pages/IMG_1274.htm HTTP/1.1" 404 133 +203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.112.85 - - [28/Feb/2008:04:42:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.165.112.85 - - [28/Feb/2008:04:42:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.119.242.94 - - [28/Feb/2008:04:44:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.177.81 - - [28/Feb/2008:04:50:25 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.81 - - [28/Feb/2008:04:50:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1035 +203.213.7.130 - - [28/Feb/2008:04:57:34 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.213.7.130 - - [28/Feb/2008:04:57:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.104 - - [28/Feb/2008:05:09:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.104 - - [28/Feb/2008:05:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +153.110.6.241 - - [28/Feb/2008:05:22:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +153.110.6.241 - - [28/Feb/2008:05:22:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.213.7.130 - - [28/Feb/2008:05:27:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.215.172.82 - - [28/Feb/2008:05:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +88.215.172.82 - - [28/Feb/2008:05:29:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.215.172.82 - - [28/Feb/2008:05:29:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +62.180.231.91 - - [28/Feb/2008:05:30:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +62.180.231.91 - - [28/Feb/2008:05:30:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +147.228.209.194 - - [28/Feb/2008:05:32:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +147.228.209.194 - - [28/Feb/2008:05:32:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20114 +89.145.209.82 - - [28/Feb/2008:05:32:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +74.6.25.199 - - [28/Feb/2008:05:39:28 -0600] "GET /cv.html HTTP/1.0" 304 - +74.6.19.156 - - [28/Feb/2008:05:40:54 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /ply/README HTTP/1.1" 200 8605 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.12.146.250 - - [28/Feb/2008:05:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.1" 404 133 +203.213.7.130 - - [28/Feb/2008:05:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.117 - - [28/Feb/2008:05:56:18 -0600] "GET /photos/wind/pages/IMG_1262.htm HTTP/1.1" 404 133 +153.110.6.241 - - [28/Feb/2008:06:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +203.213.7.130 - - [28/Feb/2008:06:09:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +59.124.68.145 - - [28/Feb/2008:06:10:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +59.124.68.145 - - [28/Feb/2008:06:10:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.124.68.145 - - [28/Feb/2008:06:10:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.13.207.74 - - [28/Feb/2008:06:12:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.13.207.74 - - [28/Feb/2008:06:12:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:06:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:06:16:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:06:16:19 -0600] "GET /cgi-bin/wiki.pl?IgnoreDirective HTTP/1.1" 200 2253 +83.13.207.74 - - [28/Feb/2008:06:17:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:06:18:20 -0600] "GET /cgi-bin/wiki.pl?NewobjectDirective HTTP/1.1" 200 4258 +80.200.221.74 - - [28/Feb/2008:06:18:29 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 +80.200.221.74 - - [28/Feb/2008:06:18:51 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +80.200.221.74 - - [28/Feb/2008:06:19:00 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 +80.200.221.74 - - [28/Feb/2008:06:19:19 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +80.200.221.74 - - [28/Feb/2008:06:19:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +80.200.221.74 - - [28/Feb/2008:06:20:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:06:21:03 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +62.180.231.91 - - [28/Feb/2008:06:21:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +80.200.221.74 - - [28/Feb/2008:06:21:28 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:06:21:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:06:21:37 -0600] "GET /cgi-bin/wiki.pl?NodefaultDirective HTTP/1.1" 200 1742 +80.200.221.74 - - [28/Feb/2008:06:21:43 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 +80.200.221.74 - - [28/Feb/2008:06:22:04 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 +80.200.221.74 - - [28/Feb/2008:06:22:22 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +80.200.221.74 - - [28/Feb/2008:06:23:06 -0600] "GET /cgi-bin/wiki.pl?ContractDirective HTTP/1.1" 200 1512 +80.200.221.74 - - [28/Feb/2008:06:23:12 -0600] "GET /cgi-bin/wiki.pl?ConstantDirective HTTP/1.1" 200 1372 +72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET /robots.txt HTTP/1.1" 200 71 +72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET / HTTP/1.1" 200 4447 +72.20.109.37 - - [28/Feb/2008:06:28:14 -0600] "GET /about.html HTTP/1.1" 200 7890 +213.95.25.246 - - [28/Feb/2008:06:30:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.95.25.246 - - [28/Feb/2008:06:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.95.25.246 - - [28/Feb/2008:06:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +213.95.25.246 - - [28/Feb/2008:06:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +213.95.25.246 - - [28/Feb/2008:06:30:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +213.95.25.246 - - [28/Feb/2008:06:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +213.95.25.246 - - [28/Feb/2008:06:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +213.95.25.246 - - [28/Feb/2008:06:31:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +71.126.89.51 - - [28/Feb/2008:06:31:19 -0600] "GET / HTTP/1.1" 200 4447 +88.191.19.81 - - [28/Feb/2008:06:31:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.247.217.34 - - [28/Feb/2008:06:32:07 -0600] "GET /robots.txt HTTP/1.1" 200 71 +61.247.217.34 - - [28/Feb/2008:06:32:08 -0600] "GET / HTTP/1.1" 200 4447 +213.95.25.246 - - [28/Feb/2008:06:32:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +213.95.25.246 - - [28/Feb/2008:06:33:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +213.95.25.246 - - [28/Feb/2008:06:34:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +203.213.7.130 - - [28/Feb/2008:06:35:40 -0600] "GET /ply/ HTTP/1.0" 304 - +64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /photos/u505/pages/IMG_1492.htm HTTP/1.0" 404 133 +128.143.136.157 - - [28/Feb/2008:06:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:06:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.104.43.5 - - [28/Feb/2008:06:38:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 +84.104.43.5 - - [28/Feb/2008:06:39:00 -0600] "GET /python.html HTTP/1.0" 200 18870 +60.50.9.191 - - [28/Feb/2008:06:39:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +60.50.9.191 - - [28/Feb/2008:06:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.93.128.164 - - [28/Feb/2008:06:44:43 -0600] "GET / HTTP/1.0" 200 4447 +91.93.128.164 - - [28/Feb/2008:06:44:44 -0600] "GET /python.html HTTP/1.0" 200 18870 +91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /training.html HTTP/1.0" 200 6154 +91.93.128.164 - - [28/Feb/2008:06:44:46 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /software.html HTTP/1.0" 200 3163 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /writing.html HTTP/1.0" 200 2871 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /index.html HTTP/1.0" 200 4447 +91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 +91.93.128.164 - - [28/Feb/2008:06:44:51 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 +91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/soln12_1.html HTTP/1.0" 404 133 +91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.0" 200 293 +91.93.128.164 - - [28/Feb/2008:06:44:54 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +129.192.97.6 - - [28/Feb/2008:06:47:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.0" 200 1352 +129.192.97.6 - - [28/Feb/2008:06:47:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 200 1620 +129.192.97.6 - - [28/Feb/2008:06:48:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.0" 200 990 +129.192.97.6 - - [28/Feb/2008:06:48:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE005.HTM HTTP/1.0" 200 1429 +129.192.97.6 - - [28/Feb/2008:06:48:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1254 +129.192.97.6 - - [28/Feb/2008:06:48:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 +129.192.97.6 - - [28/Feb/2008:06:48:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1231 +129.192.97.6 - - [28/Feb/2008:06:48:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.0" 200 1279 +129.192.97.6 - - [28/Feb/2008:06:48:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 +129.192.97.6 - - [28/Feb/2008:06:48:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:48:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE012.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:48:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE013.HTM HTTP/1.0" 200 1447 +129.192.97.6 - - [28/Feb/2008:06:48:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.0" 200 1232 +129.192.97.6 - - [28/Feb/2008:06:48:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE015.HTM HTTP/1.0" 200 1229 +129.192.97.6 - - [28/Feb/2008:06:48:44 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE016.HTM HTTP/1.0" 200 1343 +129.192.97.6 - - [28/Feb/2008:06:48:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE017.HTM HTTP/1.0" 200 1458 +129.192.97.6 - - [28/Feb/2008:06:48:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.0" 200 1429 +129.192.97.6 - - [28/Feb/2008:06:48:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 +129.192.97.6 - - [28/Feb/2008:06:48:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE020.HTM HTTP/1.0" 200 1338 +129.192.97.6 - - [28/Feb/2008:06:49:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE021.HTM HTTP/1.0" 200 1207 +129.192.97.6 - - [28/Feb/2008:06:49:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE022.HTM HTTP/1.0" 200 981 +129.192.97.6 - - [28/Feb/2008:06:49:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE023.HTM HTTP/1.0" 200 1576 +129.192.97.6 - - [28/Feb/2008:06:49:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1339 +129.192.97.6 - - [28/Feb/2008:06:49:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 +129.192.97.6 - - [28/Feb/2008:06:49:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE026.HTM HTTP/1.0" 200 1692 +129.192.97.6 - - [28/Feb/2008:06:49:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +129.192.97.6 - - [28/Feb/2008:06:49:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE028.HTM HTTP/1.0" 200 1704 +129.192.97.6 - - [28/Feb/2008:06:49:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE029.HTM HTTP/1.0" 200 1491 +129.192.97.6 - - [28/Feb/2008:06:49:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE030.HTM HTTP/1.0" 200 1549 +129.192.97.6 - - [28/Feb/2008:06:49:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE031.HTM HTTP/1.0" 200 982 +129.192.97.6 - - [28/Feb/2008:06:49:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE032.HTM HTTP/1.0" 200 1444 +60.50.9.191 - - [28/Feb/2008:06:50:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.192.97.6 - - [28/Feb/2008:06:50:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE033.HTM HTTP/1.0" 200 1804 +60.50.9.191 - - [28/Feb/2008:06:50:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.192.97.6 - - [28/Feb/2008:06:50:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.0" 200 1586 +129.192.97.6 - - [28/Feb/2008:06:50:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1441 +129.192.97.6 - - [28/Feb/2008:06:51:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE036.HTM HTTP/1.0" 200 2010 +129.192.97.6 - - [28/Feb/2008:06:51:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE037.HTM HTTP/1.0" 200 1772 +129.192.97.6 - - [28/Feb/2008:06:51:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.0" 200 1373 +129.192.97.6 - - [28/Feb/2008:06:51:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE039.HTM HTTP/1.0" 200 1638 +129.192.97.6 - - [28/Feb/2008:06:51:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE040.HTM HTTP/1.0" 200 1910 +129.192.97.6 - - [28/Feb/2008:06:51:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE041.HTM HTTP/1.0" 200 2037 +129.192.97.6 - - [28/Feb/2008:06:51:46 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.0" 200 1336 +129.192.97.6 - - [28/Feb/2008:06:51:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE043.HTM HTTP/1.0" 200 1368 +129.192.97.6 - - [28/Feb/2008:06:51:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE044.HTM HTTP/1.0" 200 1000 +129.192.97.6 - - [28/Feb/2008:06:51:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 +129.192.97.6 - - [28/Feb/2008:06:51:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1738 +129.192.97.6 - - [28/Feb/2008:06:52:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE047.HTM HTTP/1.0" 200 1410 +129.192.97.6 - - [28/Feb/2008:06:52:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE048.HTM HTTP/1.0" 200 1654 +129.192.97.6 - - [28/Feb/2008:06:52:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE049.HTM HTTP/1.0" 200 1574 +129.192.97.6 - - [28/Feb/2008:06:52:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE050.HTM HTTP/1.0" 200 989 +129.192.97.6 - - [28/Feb/2008:06:52:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE051.HTM HTTP/1.0" 200 1476 +129.192.97.6 - - [28/Feb/2008:06:52:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1633 +129.192.97.6 - - [28/Feb/2008:06:52:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1661 +129.192.97.6 - - [28/Feb/2008:06:52:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE054.HTM HTTP/1.0" 200 1450 +129.192.97.6 - - [28/Feb/2008:06:52:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE055.HTM HTTP/1.0" 200 1319 +129.192.97.6 - - [28/Feb/2008:06:52:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE056.HTM HTTP/1.0" 200 1504 +129.192.97.6 - - [28/Feb/2008:06:52:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE057.HTM HTTP/1.0" 200 1847 +129.192.97.6 - - [28/Feb/2008:06:52:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.0" 200 1296 +213.95.25.246 - - [28/Feb/2008:06:52:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +129.192.97.6 - - [28/Feb/2008:06:52:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE059.HTM HTTP/1.0" 200 1694 +129.192.97.6 - - [28/Feb/2008:06:52:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE060.HTM HTTP/1.0" 200 1686 +129.192.97.6 - - [28/Feb/2008:06:52:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 +129.192.97.6 - - [28/Feb/2008:06:52:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.0" 200 1322 +129.192.97.6 - - [28/Feb/2008:06:52:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE063.HTM HTTP/1.0" 200 1511 +129.192.97.6 - - [28/Feb/2008:06:52:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE064.HTM HTTP/1.0" 200 971 +129.192.97.6 - - [28/Feb/2008:06:52:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE065.HTM HTTP/1.0" 200 1822 +129.192.97.6 - - [28/Feb/2008:06:52:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE066.HTM HTTP/1.0" 200 1538 +129.192.97.6 - - [28/Feb/2008:06:52:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE067.HTM HTTP/1.0" 200 1750 +129.192.97.6 - - [28/Feb/2008:06:52:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 +129.192.97.6 - - [28/Feb/2008:06:52:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE069.HTM HTTP/1.0" 200 1702 +129.192.97.6 - - [28/Feb/2008:06:52:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE070.HTM HTTP/1.0" 200 1698 +129.192.97.6 - - [28/Feb/2008:06:52:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1659 +129.192.97.6 - - [28/Feb/2008:06:52:57 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 +129.192.97.6 - - [28/Feb/2008:06:52:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE073.HTM HTTP/1.0" 200 1290 +129.192.97.6 - - [28/Feb/2008:06:53:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE074.HTM HTTP/1.0" 200 1351 +129.192.97.6 - - [28/Feb/2008:06:53:03 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE075.HTM HTTP/1.0" 200 1512 +129.192.97.6 - - [28/Feb/2008:06:53:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1650 +129.192.97.6 - - [28/Feb/2008:06:53:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE077.HTM HTTP/1.0" 200 1451 +129.192.97.6 - - [28/Feb/2008:06:53:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE078.HTM HTTP/1.0" 200 1329 +129.192.97.6 - - [28/Feb/2008:06:53:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE079.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:53:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE080.HTM HTTP/1.0" 200 1578 +129.192.97.6 - - [28/Feb/2008:06:53:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 +129.192.97.6 - - [28/Feb/2008:06:53:18 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE082.HTM HTTP/1.0" 200 983 +129.192.97.6 - - [28/Feb/2008:06:53:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 +129.192.97.6 - - [28/Feb/2008:06:53:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1475 +129.192.97.6 - - [28/Feb/2008:06:53:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE085.HTM HTTP/1.0" 200 1739 +129.192.97.6 - - [28/Feb/2008:06:53:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE086.HTM HTTP/1.0" 200 1726 +129.192.97.6 - - [28/Feb/2008:06:53:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 +129.192.97.6 - - [28/Feb/2008:06:53:45 -0600] "GET /python/tutorial/beazley_advanced_python/ HTTP/1.0" 403 238 +213.95.25.246 - - [28/Feb/2008:06:53:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +129.192.97.6 - - [28/Feb/2008:06:53:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 +129.192.97.6 - - [28/Feb/2008:06:53:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE089.HTM HTTP/1.0" 200 1444 +129.192.97.6 - - [28/Feb/2008:06:53:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE090.HTM HTTP/1.0" 200 2001 +129.192.97.6 - - [28/Feb/2008:06:54:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 +129.192.97.6 - - [28/Feb/2008:06:54:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE092.HTM HTTP/1.0" 200 1427 +129.192.97.6 - - [28/Feb/2008:06:54:15 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE093.HTM HTTP/1.0" 200 1412 +129.192.97.6 - - [28/Feb/2008:06:54:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE094.HTM HTTP/1.0" 200 1435 +129.192.97.6 - - [28/Feb/2008:06:54:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 +129.192.97.6 - - [28/Feb/2008:06:54:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE096.HTM HTTP/1.0" 200 1671 +129.192.97.6 - - [28/Feb/2008:06:54:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 +129.192.97.6 - - [28/Feb/2008:06:54:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 +129.192.97.6 - - [28/Feb/2008:06:54:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE099.HTM HTTP/1.0" 200 1493 +129.192.97.6 - - [28/Feb/2008:06:54:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE100.HTM HTTP/1.0" 200 1562 +129.192.97.6 - - [28/Feb/2008:06:54:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE101.HTM HTTP/1.0" 200 980 +129.192.97.6 - - [28/Feb/2008:06:54:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE102.HTM HTTP/1.0" 200 1252 +129.192.97.6 - - [28/Feb/2008:06:54:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE103.HTM HTTP/1.0" 200 1330 +129.192.97.6 - - [28/Feb/2008:06:54:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 +129.192.97.6 - - [28/Feb/2008:06:54:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE105.HTM HTTP/1.0" 200 1256 +129.192.97.6 - - [28/Feb/2008:06:54:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 +129.192.97.6 - - [28/Feb/2008:06:54:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE107.HTM HTTP/1.0" 200 1397 +129.192.97.6 - - [28/Feb/2008:06:55:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE108.HTM HTTP/1.0" 200 1686 +129.192.97.6 - - [28/Feb/2008:06:55:01 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 +129.192.97.6 - - [28/Feb/2008:06:55:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE110.HTM HTTP/1.0" 200 1791 +129.192.97.6 - - [28/Feb/2008:06:55:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE111.HTM HTTP/1.0" 200 1431 +129.192.97.6 - - [28/Feb/2008:06:55:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE112.HTM HTTP/1.0" 200 987 +129.192.97.6 - - [28/Feb/2008:06:55:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1134 +129.192.97.6 - - [28/Feb/2008:06:55:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE114.HTM HTTP/1.0" 200 1821 +129.192.97.6 - - [28/Feb/2008:06:55:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 +129.192.97.6 - - [28/Feb/2008:06:55:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE116.HTM HTTP/1.0" 200 1585 +129.192.97.6 - - [28/Feb/2008:06:55:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE117.HTM HTTP/1.0" 200 979 +129.192.97.6 - - [28/Feb/2008:06:55:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE118.HTM HTTP/1.0" 200 1437 +38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET / HTTP/1.1" 200 4447 +129.192.97.6 - - [28/Feb/2008:06:55:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +129.192.97.6 - - [28/Feb/2008:06:55:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE120.HTM HTTP/1.0" 200 1308 +129.192.97.6 - - [28/Feb/2008:06:55:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 +129.192.97.6 - - [28/Feb/2008:06:56:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE122.HTM HTTP/1.0" 200 1297 +129.192.97.6 - - [28/Feb/2008:06:56:13 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE123.HTM HTTP/1.0" 200 1195 +129.192.97.6 - - [28/Feb/2008:06:56:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 +129.192.97.6 - - [28/Feb/2008:06:56:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE125.HTM HTTP/1.0" 200 978 +129.192.97.6 - - [28/Feb/2008:06:56:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE126.HTM HTTP/1.0" 200 1555 +129.192.97.6 - - [28/Feb/2008:06:56:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE127.HTM HTTP/1.0" 404 133 +129.192.97.6 - - [28/Feb/2008:06:56:32 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.0" 403 245 +38.98.120.84 - - [28/Feb/2008:06:57:28 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:57:29 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:57:30 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:31 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:38 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:57:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:57:40 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +192.88.162.35 - - [28/Feb/2008:06:57:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:57:44 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:46 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:47 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:48 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +129.192.97.6 - - [28/Feb/2008:06:57:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1225 +38.98.120.84 - - [28/Feb/2008:06:57:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:51 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:06:58:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:06:58:06 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:07 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:08 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:58:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:58:12 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:13 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:16 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:17 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:19 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:20 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:21 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:22 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:23 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:26 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:06:58:27 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [28/Feb/2008:06:58:29 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:31 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:06:58:32 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:33 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:34 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:06:58:37 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:58:39 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:58:41 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:42 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:06:58:43 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:44 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:46 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:06:58:47 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:48 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 +38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:53 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:56 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:57 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:06:59:00 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:01 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:59:03 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:04 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:08 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 +38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:14 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:15 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:16 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:17 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:18 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:19 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:06:59:21 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:06:59:22 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [28/Feb/2008:06:59:23 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:25 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:26 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:27 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:28 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:29 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:06:59:30 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:32 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:33 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:34 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:59:36 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:38 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:06:59:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:06:59:40 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:41 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:43 -0600] "GET /diet.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:46 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:06:59:47 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 +38.98.120.84 - - [28/Feb/2008:06:59:48 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:52 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +38.98.120.84 - - [28/Feb/2008:06:59:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:54 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:55 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:56 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [28/Feb/2008:06:59:58 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:59 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:00:00 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:00:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:00:02 -0600] "GET /swill/software.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:00:03 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:04 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:00:05 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:06 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:00:07 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +38.98.120.84 - - [28/Feb/2008:07:00:08 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:00:09 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:10 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:23 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 122740 +86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:00:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 112604 +38.98.120.84 - - [28/Feb/2008:07:01:16 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:17 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:01:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:01:20 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:01:21 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:01:22 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:01:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:01:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:01:25 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:01:26 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:01:27 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:31 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:32 -0600] "GET /about HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:07:01:34 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:07:01:35 -0600] "GET /assign5.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:01:37 -0600] "GET /dynamic/assign5 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:38 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:39 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:41 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:42 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc HTTP/1.1" 301 248 +38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:44 -0600] "GET /swill/Doc/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:46 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc HTTP/1.1" 301 248 +38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:48 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:49 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:50 -0600] "GET /swill/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:52 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:53 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:54 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:55 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:56 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:57 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:07:01:58 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:07:01:59 -0600] "GET /dynamic/sd HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:01 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:02 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [28/Feb/2008:07:02:03 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +65.55.208.118 - - [28/Feb/2008:07:02:04 -0600] "GET /photos/u505/pages/IMG_1510.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:04 -0600] "GET /dynamic/smackdown HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:06 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:07 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:07:02:08 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:07:02:09 -0600] "GET /papers/Py97/beazley HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:10 -0600] "GET /Py97/beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:11 -0600] "GET /papers/beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97 HTTP/1.1" 301 250 +38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97/ HTTP/1.1" 403 222 +38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:02:14 -0600] "GET /beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:15 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:02:16 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:02:17 -0600] "GET /training HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:18 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:07:02:19 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:02:21 -0600] "GET /papers/Perl98/swigperl HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:22 -0600] "GET /papers/swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98 HTTP/1.1" 301 252 +38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98/ HTTP/1.1" 403 224 +38.98.120.84 - - [28/Feb/2008:07:02:24 -0600] "GET /swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:25 -0600] "GET /Perl98/swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:29 -0600] "GET /dynamic/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:30 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:31 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:07:02:32 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:38 -0600] "GET /dynamic/assign3 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:39 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:40 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:41 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:42 -0600] "GET /index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:43 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:02:44 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:02:45 -0600] "GET /software HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:46 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:47 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:02:48 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:02:49 -0600] "GET /README HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:52 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:53 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:07:02:54 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:56 -0600] "GET /ply/support HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:57 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:58 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:07:02:59 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:01 -0600] "GET /dynamic/assign4 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:02 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:03:04 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:03:05 -0600] "GET /writing HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:07:03:07 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:09 -0600] "GET /dynamic/syllabus HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:10 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:11 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:03:12 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:14 -0600] "GET /papers/python.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001 HTTP/1.1" 301 256 +38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001/ HTTP/1.1" 403 228 +38.98.120.84 - - [28/Feb/2008:07:03:16 -0600] "GET /papers/Python2001/python HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:17 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:03:18 -0600] "GET /Python2001/python.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:19 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:07:03:20 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:07:03:21 -0600] "GET /papers/tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96 HTTP/1.1" 301 251 +38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96/ HTTP/1.1" 403 223 +38.98.120.84 - - [28/Feb/2008:07:03:24 -0600] "GET /papers/Tcl96/tcl96 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:29 -0600] "GET /tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:30 -0600] "GET /Tcl96/tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:07:03:32 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:07:03:33 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:34 -0600] "GET /per HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:35 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:39 -0600] "GET /sysop HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:40 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:07:03:41 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:43 -0600] "GET /dynamic/assign2 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:44 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:45 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:03:46 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:03:47 -0600] "GET /papers/python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96 HTTP/1.1" 301 250 +38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96/ HTTP/1.1" 403 222 +38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /papers/Py96/python96 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:52 -0600] "GET /Py96/python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:04 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:07:04:05 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:07 -0600] "GET /dynamic/assign1 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:08 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:07:04:10 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:26 -0600] "GET /dynamic/soln1 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:27 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:28 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:07:04:35 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:49 -0600] "GET /dynamic/dowstocks HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:50 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:52 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:56 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:04:57 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:04:58 -0600] "GET /papers/TclChap.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98 HTTP/1.1" 301 251 +38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98/ HTTP/1.1" 403 223 +38.98.120.84 - - [28/Feb/2008:07:05:01 -0600] "GET /papers/Tcl98/TclChap HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:02 -0600] "GET /TclChap.html HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:07:05:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:11 -0600] "GET /Tcl98/TclChap.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:24 -0600] "GET /cv HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:26 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/ HTTP/1.0" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:27 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +200.19.92.58 - - [28/Feb/2008:07:05:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:05:29 -0600] "GET /swill/exec HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:30 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:07:05:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:34 -0600] "GET /ply/ply HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:35 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:05:36 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:05:37 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:38 -0600] "GET /publications HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:05:40 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:05:41 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:42 -0600] "GET /consulting HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:43 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:05:44 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:05:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:52 -0600] "GET /ply/example HTTP/1.1" 404 133 +74.6.25.234 - - [28/Feb/2008:07:06:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 304 - +200.19.92.58 - - [28/Feb/2008:07:09:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.161.167.222 - - [28/Feb/2008:07:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET / HTTP/1.1" 200 4447 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.229.225 - - [28/Feb/2008:07:09:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +68.252.229.225 - - [28/Feb/2008:07:09:28 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +65.55.208.118 - - [28/Feb/2008:07:09:38 -0600] "GET /photos/u505/pages/IMG_1538.htm HTTP/1.1" 404 133 +74.6.24.10 - - [28/Feb/2008:07:11:23 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.0" 304 - +65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1278.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1256.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:13:45 -0600] "GET /photos/wind/pages/IMG_1304.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:14:41 -0600] "GET /photos/wind/pages/IMG_1295.htm HTTP/1.1" 404 133 +193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.174.238.115 - - [28/Feb/2008:07:14:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +65.214.45.129 - - [28/Feb/2008:07:17:23 -0600] "GET /python/per_secrets.html HTTP/1.0" 404 133 +60.50.9.191 - - [28/Feb/2008:07:19:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:07:21:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:07:24:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.161.167.222 - - [28/Feb/2008:07:24:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.161.167.222 - - [28/Feb/2008:07:24:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:07:24:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +59.96.12.231 - - [28/Feb/2008:07:26:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +59.96.12.231 - - [28/Feb/2008:07:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [28/Feb/2008:07:27:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +82.127.117.160 - - [28/Feb/2008:07:28:11 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:07:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +217.10.60.85 - - [28/Feb/2008:07:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +217.10.60.85 - - [28/Feb/2008:07:31:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.10.60.85 - - [28/Feb/2008:07:31:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +217.10.60.85 - - [28/Feb/2008:07:31:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.10.60.85 - - [28/Feb/2008:07:31:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +217.10.60.85 - - [28/Feb/2008:07:31:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +60.50.9.191 - - [28/Feb/2008:07:32:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:07:32:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 +217.10.60.85 - - [28/Feb/2008:07:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +217.10.60.85 - - [28/Feb/2008:07:32:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +65.214.45.129 - - [28/Feb/2008:07:41:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 +68.252.229.225 - - [28/Feb/2008:07:44:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.141.224.186 - - [28/Feb/2008:07:46:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.141.224.186 - - [28/Feb/2008:07:47:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +83.170.97.200 - - [28/Feb/2008:07:47:35 -0600] "GET / HTTP/1.0" 200 4447 +83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /training.html HTTP/1.0" 200 6154 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /python.html HTTP/1.0" 200 18870 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /index.html HTTP/1.0" 200 4447 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /writing.html HTTP/1.0" 200 2871 +83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /about.html HTTP/1.0" 200 7890 +83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 +83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /cv.html HTTP/1.0" 200 31798 +83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 +82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.95.165.247 - - [28/Feb/2008:07:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.95.165.247 - - [28/Feb/2008:07:49:08 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +201.236.226.90 - - [28/Feb/2008:07:51:01 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +82.95.165.247 - - [28/Feb/2008:07:53:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.200.221.74 - - [28/Feb/2008:07:57:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:07:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:07:57:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:07:57:43 -0600] "GET /cgi-bin/wiki.pl?RenameDirective HTTP/1.1" 200 3672 +83.12.146.250 - - [28/Feb/2008:08:00:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:08:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.79.171.46 - - [28/Feb/2008:08:07:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ModuleDirective HTTP/1.1" 200 2302 +222.221.6.144 - - [28/Feb/2008:08:07:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +24.1.159.241 - - [28/Feb/2008:08:08:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +24.1.159.241 - - [28/Feb/2008:08:08:20 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +80.200.221.74 - - [28/Feb/2008:08:09:20 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +69.147.83.113 - - [28/Feb/2008:08:10:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.209.8.145 - - [28/Feb/2008:08:11:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.209.8.145 - - [28/Feb/2008:08:11:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +219.136.230.60 - - [28/Feb/2008:08:14:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 - +220.150.154.145 - - [28/Feb/2008:08:14:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +220.150.154.145 - - [28/Feb/2008:08:14:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:08:16:22 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 +80.200.221.74 - - [28/Feb/2008:08:16:39 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 +208.51.93.163 - - [28/Feb/2008:08:16:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +208.51.93.163 - - [28/Feb/2008:08:16:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:08:16:53 -0600] "GET /cgi-bin/wiki.pl?NameDirective HTTP/1.1" 200 1785 +80.200.221.74 - - [28/Feb/2008:08:17:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +80.200.221.74 - - [28/Feb/2008:08:17:08 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 +65.55.212.77 - - [28/Feb/2008:08:19:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.19.101 - - [28/Feb/2008:08:20:10 -0600] "GET /swill/Doc/ HTTP/1.0" 304 - +82.127.117.160 - - [28/Feb/2008:08:21:46 -0600] "GET /ply HTTP/1.0" 301 230 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:27:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.119 - - [28/Feb/2008:08:28:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 +193.128.72.68 - - [28/Feb/2008:08:28:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:29:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:30:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +138.100.218.23 - - [28/Feb/2008:08:30:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.41.248 - - [28/Feb/2008:08:33:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +69.217.73.2 - - [28/Feb/2008:08:34:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.217.73.2 - - [28/Feb/2008:08:34:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.21.88.201 - - [28/Feb/2008:08:35:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.21.88.201 - - [28/Feb/2008:08:35:04 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +69.217.73.2 - - [28/Feb/2008:08:36:16 -0600] "GET / HTTP/1.1" 200 4447 +69.217.73.2 - - [28/Feb/2008:08:36:17 -0600] "GET /index.html HTTP/1.1" 200 4447 +69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /training.html HTTP/1.1" 200 6154 +69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /software.html HTTP/1.1" 200 3163 +69.217.73.2 - - [28/Feb/2008:08:36:20 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +69.217.73.2 - - [28/Feb/2008:08:36:21 -0600] "GET /writing.html HTTP/1.1" 200 2871 +69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /about.html HTTP/1.1" 200 7890 +69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.217.73.2 - - [28/Feb/2008:08:36:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +69.217.73.2 - - [28/Feb/2008:08:36:25 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:48 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.229.38.64 - - [28/Feb/2008:08:44:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +64.124.85.71 - - [28/Feb/2008:08:45:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 +82.95.165.247 - - [28/Feb/2008:08:52:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +216.201.139.126 - - [28/Feb/2008:08:54:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.127.117.160 - - [28/Feb/2008:08:55:29 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.78.145.1 - - [28/Feb/2008:09:03:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:06:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.138.208.6 - - [28/Feb/2008:09:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.138.208.6 - - [28/Feb/2008:09:11:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [28/Feb/2008:09:14:22 -0600] "GET /ply/ HTTP/1.0" 304 - +128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.139.150 - - [28/Feb/2008:09:17:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +193.138.208.6 - - [28/Feb/2008:09:17:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.0.96.15 - - [28/Feb/2008:09:20:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.0.96.15 - - [28/Feb/2008:09:21:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +193.0.96.15 - - [28/Feb/2008:09:21:37 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +128.221.197.20 - - [28/Feb/2008:09:25:33 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +150.210.155.167 - - [28/Feb/2008:09:25:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.161.167.222 - - [28/Feb/2008:09:26:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +65.55.208.119 - - [28/Feb/2008:09:28:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.1" 304 - +193.0.96.15 - - [28/Feb/2008:09:32:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +129.194.8.73 - - [28/Feb/2008:09:32:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.168.117.112 - - [28/Feb/2008:09:35:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +84.168.117.112 - - [28/Feb/2008:09:35:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.97.120.226 - - [28/Feb/2008:09:37:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.97.120.226 - - [28/Feb/2008:09:37:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.168.117.112 - - [28/Feb/2008:09:37:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +84.168.117.112 - - [28/Feb/2008:09:37:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.80.22.103 - - [28/Feb/2008:09:39:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +195.80.22.103 - - [28/Feb/2008:09:40:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +195.80.22.103 - - [28/Feb/2008:09:40:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +129.194.8.73 - - [28/Feb/2008:09:40:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:51 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [28/Feb/2008:09:42:53 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +88.80.205.215 - - [28/Feb/2008:09:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.80.205.215 - - [28/Feb/2008:09:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +98.193.69.179 - - [28/Feb/2008:09:43:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:09:43:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +193.0.96.15 - - [28/Feb/2008:09:44:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.0.96.15 - - [28/Feb/2008:09:44:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +65.55.208.119 - - [28/Feb/2008:09:46:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:46:38 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE012.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:46:58 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:47:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.1" 304 - +82.119.242.94 - - [28/Feb/2008:09:48:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:09:55:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.71.37.92 - - [28/Feb/2008:09:56:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.76.29.2 - - [28/Feb/2008:09:57:21 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1773 +79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /cv.html HTTP/1.1" 200 31798 +79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.76.29.2 - - [28/Feb/2008:09:57:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +194.76.29.2 - - [28/Feb/2008:09:58:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.0" 200 2320 +194.76.29.2 - - [28/Feb/2008:09:58:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.76.29.2 - - [28/Feb/2008:09:58:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:09:58:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:09:58:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:10:00:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 +194.76.29.2 - - [28/Feb/2008:10:01:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAIXSharedLibraries HTTP/1.0" 200 3494 +64.234.244.162 - - [28/Feb/2008:10:02:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.0.96.15 - - [28/Feb/2008:10:04:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +193.0.96.15 - - [28/Feb/2008:10:05:08 -0600] "GET /python.html HTTP/1.0" 200 18870 +193.0.96.15 - - [28/Feb/2008:10:05:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +64.236.139.6 - - [28/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +64.236.139.6 - - [28/Feb/2008:10:07:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.236.139.6 - - [28/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +64.236.139.6 - - [28/Feb/2008:10:07:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +193.0.96.15 - - [28/Feb/2008:10:07:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.236.139.6 - - [28/Feb/2008:10:07:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +128.221.197.20 - - [28/Feb/2008:10:08:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +64.236.139.6 - - [28/Feb/2008:10:09:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +64.236.139.6 - - [28/Feb/2008:10:09:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +64.236.139.6 - - [28/Feb/2008:10:10:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +64.236.139.6 - - [28/Feb/2008:10:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +64.236.139.6 - - [28/Feb/2008:10:13:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +64.236.139.6 - - [28/Feb/2008:10:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +98.193.69.179 - - [28/Feb/2008:10:16:52 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +89.189.65.147 - - [28/Feb/2008:10:17:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.189.65.147 - - [28/Feb/2008:10:18:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.168.9.102 - - [28/Feb/2008:10:23:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +132.168.9.102 - - [28/Feb/2008:10:23:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +132.168.9.102 - - [28/Feb/2008:10:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +194.76.29.2 - - [28/Feb/2008:10:24:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +128.221.197.20 - - [28/Feb/2008:10:29:52 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +70.249.147.120 - - [28/Feb/2008:10:31:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.249.147.120 - - [28/Feb/2008:10:31:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.249.147.120 - - [28/Feb/2008:10:32:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [28/Feb/2008:10:33:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +128.221.197.20 - - [28/Feb/2008:10:33:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +81.52.143.15 - - [28/Feb/2008:10:34:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +81.52.143.15 - - [28/Feb/2008:10:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +193.158.76.56 - - [28/Feb/2008:10:36:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.158.76.56 - - [28/Feb/2008:10:36:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:36:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:36:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +193.158.76.56 - - [28/Feb/2008:10:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +193.158.76.56 - - [28/Feb/2008:10:36:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +193.158.76.56 - - [28/Feb/2008:10:36:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +193.158.76.56 - - [28/Feb/2008:10:36:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +193.158.76.56 - - [28/Feb/2008:10:37:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:37:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.71.33.174 - - [28/Feb/2008:10:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +207.71.33.174 - - [28/Feb/2008:10:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +64.234.244.162 - - [28/Feb/2008:10:39:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:10:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:19 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +98.215.100.68 - - [28/Feb/2008:10:43:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.1.67.100 - - [28/Feb/2008:10:43:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.1.67.100 - - [28/Feb/2008:10:43:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.244.205.42 - - [28/Feb/2008:10:43:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +99.244.205.42 - - [28/Feb/2008:10:43:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:49 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +208.36.144.7 - - [28/Feb/2008:10:44:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 +66.1.67.100 - - [28/Feb/2008:10:44:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [28/Feb/2008:10:45:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +148.233.159.58 - - [28/Feb/2008:10:45:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +66.1.67.100 - - [28/Feb/2008:10:45:45 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +99.244.205.42 - - [28/Feb/2008:10:46:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.149.16.155 - - [28/Feb/2008:10:49:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +80.149.16.155 - - [28/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.149.16.155 - - [28/Feb/2008:10:50:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.212 - - [28/Feb/2008:10:50:06 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +69.46.29.140 - - [28/Feb/2008:10:50:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 +80.97.94.178 - - [28/Feb/2008:10:50:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.221.197.20 - - [28/Feb/2008:10:52:34 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.24.63.240 - - [28/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [28/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +65.166.139.21 - - [28/Feb/2008:10:55:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.166.139.21 - - [28/Feb/2008:10:55:25 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +65.166.139.21 - - [28/Feb/2008:10:55:32 -0600] "GET /cgi-bin/wiki.pl?DavidBeazley HTTP/1.1" 200 2019 +65.166.139.21 - - [28/Feb/2008:10:55:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +65.166.139.21 - - [28/Feb/2008:10:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +65.166.139.21 - - [28/Feb/2008:10:56:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +65.166.139.21 - - [28/Feb/2008:10:57:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +65.166.139.21 - - [28/Feb/2008:10:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.1" 200 2861 +65.166.139.21 - - [28/Feb/2008:10:57:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 +65.166.139.21 - - [28/Feb/2008:10:58:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 +65.166.139.21 - - [28/Feb/2008:10:58:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +65.166.139.21 - - [28/Feb/2008:10:58:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +64.234.244.162 - - [28/Feb/2008:11:02:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.54.202.234 - - [28/Feb/2008:11:05:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +196.25.255.210 - - [28/Feb/2008:11:05:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.246 - - [28/Feb/2008:11:05:57 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +196.25.255.210 - - [28/Feb/2008:11:06:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [28/Feb/2008:11:07:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +196.25.255.210 - - [28/Feb/2008:11:07:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +198.54.202.218 - - [28/Feb/2008:11:07:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +196.25.255.194 - - [28/Feb/2008:11:08:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +196.25.255.250 - - [28/Feb/2008:11:08:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +196.25.255.210 - - [28/Feb/2008:11:08:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +24.7.210.64 - - [28/Feb/2008:11:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [28/Feb/2008:11:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +24.7.210.64 - - [28/Feb/2008:11:11:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.120 - - [28/Feb/2008:11:12:53 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +192.109.190.88 - - [28/Feb/2008:11:13:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:11:20:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.1" 304 - +208.36.144.7 - - [28/Feb/2008:11:21:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.139.146 - - [28/Feb/2008:11:30:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.55.208.117 - - [28/Feb/2008:11:34:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 304 - +198.253.24.5 - - [28/Feb/2008:11:34:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +198.253.24.5 - - [28/Feb/2008:11:34:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.253.24.5 - - [28/Feb/2008:11:35:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.31.57 - - [28/Feb/2008:11:37:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +64.234.244.162 - - [28/Feb/2008:11:40:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.77.240.100 - - [28/Feb/2008:11:43:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +151.77.240.100 - - [28/Feb/2008:11:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +151.77.240.100 - - [28/Feb/2008:11:43:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +52.128.30.11 - - [28/Feb/2008:11:46:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.195.58.158 - - [28/Feb/2008:12:02:22 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET / HTTP/1.0" 200 4447 +91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET /index.html HTTP/1.0" 200 4447 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /about.html HTTP/1.0" 200 7890 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /writing.html HTTP/1.0" 200 2871 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /software.html HTTP/1.0" 200 3163 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /python.html HTTP/1.0" 200 18870 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /training.html HTTP/1.0" 200 6154 +91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /publications.html HTTP/1.0" 200 7758 +91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +91.121.19.229 - - [28/Feb/2008:12:05:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /papers/Py97/beazley.html HTTP/1.0" 200 31315 +91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +192.54.144.229 - - [28/Feb/2008:12:06:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:12:07:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [28/Feb/2008:12:07:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.11.245 - - [28/Feb/2008:12:09:58 -0600] "GET /dynamic/ HTTP/1.0" 200 5313 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /python.html HTTP/1.0" 200 18870 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.234.244.162 - - [28/Feb/2008:12:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.234.244.162 - - [28/Feb/2008:12:16:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.232.14 - - [28/Feb/2008:12:18:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.234.244.162 - - [28/Feb/2008:12:22:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 +76.222.192.62 - - [28/Feb/2008:12:26:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.56.210.71 - - [28/Feb/2008:12:26:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.56.210.71 - - [28/Feb/2008:12:26:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.56.210.71 - - [28/Feb/2008:12:26:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [28/Feb/2008:12:28:18 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [28/Feb/2008:12:28:25 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +76.222.192.62 - - [28/Feb/2008:12:28:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.56.210.71 - - [28/Feb/2008:12:28:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:12:29:27 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE048.HTM HTTP/1.1" 304 - +132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:31:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +132.206.52.79 - - [28/Feb/2008:12:36:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +132.206.52.79 - - [28/Feb/2008:12:36:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.88.143.1 - - [28/Feb/2008:12:36:51 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 +74.56.210.71 - - [28/Feb/2008:12:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.118.156.49 - - [28/Feb/2008:12:39:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.118.156.49 - - [28/Feb/2008:12:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.118.156.49 - - [28/Feb/2008:12:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.118.156.49 - - [28/Feb/2008:12:39:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 248228 +71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET / HTTP/1.1" 200 4447 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [28/Feb/2008:12:41:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.24.9 - - [28/Feb/2008:12:41:23 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /python.html HTTP/1.1" 200 18870 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /python.html HTTP/1.1" 304 - +208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - +84.110.206.115 - - [28/Feb/2008:12:50:59 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +208.97.218.10 - - [28/Feb/2008:12:51:01 -0600] "GET /software.html HTTP/1.1" 200 3163 +84.110.206.115 - - [28/Feb/2008:12:51:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1247 +76.28.208.159 - - [28/Feb/2008:12:51:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:12:52:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.189.94 - - [28/Feb/2008:12:56:01 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.94 - - [28/Feb/2008:12:56:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1283 +151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:03:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.28.208.159 - - [28/Feb/2008:13:03:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +151.200.90.2 - - [28/Feb/2008:13:04:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +151.200.90.2 - - [28/Feb/2008:13:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +76.28.208.159 - - [28/Feb/2008:13:04:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.28.208.159 - - [28/Feb/2008:13:04:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:04:46 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:13:04:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:04:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:05:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [28/Feb/2008:13:05:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.200.90.2 - - [28/Feb/2008:13:06:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 127268 +71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 +74.56.210.71 - - [28/Feb/2008:13:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:12:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:15:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:13:17:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:18:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +68.21.20.162 - - [28/Feb/2008:13:21:40 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:13:21:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.21.20.162 - - [28/Feb/2008:13:21:53 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +76.28.208.159 - - [28/Feb/2008:13:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /cv.html HTTP/1.1" 200 31798 +122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:13:27:21 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 +122.167.72.204 - - [28/Feb/2008:13:29:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:29:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:29:22 -0600] "GET / HTTP/1.1" 200 4447 +122.167.72.204 - - [28/Feb/2008:13:29:25 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +122.167.72.204 - - [28/Feb/2008:13:30:28 -0600] "GET /software.html HTTP/1.1" 200 3163 +122.167.72.204 - - [28/Feb/2008:13:30:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +122.167.72.204 - - [28/Feb/2008:13:30:42 -0600] "GET /writing.html HTTP/1.1" 200 2871 +122.167.72.204 - - [28/Feb/2008:13:30:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +122.167.72.204 - - [28/Feb/2008:13:30:48 -0600] "GET /about.html HTTP/1.1" 200 7890 +76.28.208.159 - - [28/Feb/2008:13:30:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:30:56 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +122.167.72.204 - - [28/Feb/2008:13:30:59 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +122.167.72.204 - - [28/Feb/2008:13:31:00 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +64.234.244.162 - - [28/Feb/2008:13:31:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +122.167.72.204 - - [28/Feb/2008:13:31:04 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +122.167.72.204 - - [28/Feb/2008:13:32:11 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +67.186.98.20 - - [28/Feb/2008:13:32:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:32:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.95.184 - - [28/Feb/2008:13:33:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.95.184 - - [28/Feb/2008:13:34:08 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +220.225.53.35 - - [28/Feb/2008:13:34:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:35:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:35:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:36:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:36:32 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:46:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +122.167.72.204 - - [28/Feb/2008:13:51:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:54:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.224.53 - - [28/Feb/2008:13:54:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +122.167.72.204 - - [28/Feb/2008:13:55:20 -0600] "GET /index.html HTTP/1.1" 200 4447 +122.167.72.204 - - [28/Feb/2008:13:55:27 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.186.98.20 - - [28/Feb/2008:13:57:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.64.212.223 - - [28/Feb/2008:13:59:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +81.64.212.223 - - [28/Feb/2008:13:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.64.212.223 - - [28/Feb/2008:13:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.175.55.5 - - [28/Feb/2008:13:59:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.53.107.51 - - [28/Feb/2008:14:01:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:14:06:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.2.96.93 - - [28/Feb/2008:14:12:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.2.96.93 - - [28/Feb/2008:14:12:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.166.139.21 - - [28/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +65.166.139.21 - - [28/Feb/2008:14:12:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +65.166.139.21 - - [28/Feb/2008:14:13:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +61.247.217.38 - - [28/Feb/2008:14:17:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +67.186.98.20 - - [28/Feb/2008:14:17:56 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.186.98.20 - - [28/Feb/2008:14:19:13 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.49.96.242 - - [28/Feb/2008:14:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.49.96.242 - - [28/Feb/2008:14:21:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +91.49.96.242 - - [28/Feb/2008:14:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +64.233.178.136 - - [28/Feb/2008:14:23:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +190.18.132.71 - - [28/Feb/2008:14:24:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.105.175.11 - - [28/Feb/2008:14:25:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:14:26:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:14:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:14:27:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:14:27:48 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +84.110.127.32 - - [28/Feb/2008:14:29:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.127.32 - - [28/Feb/2008:14:29:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 996 +74.6.31.151 - - [28/Feb/2008:14:31:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +209.17.146.129 - - [28/Feb/2008:14:34:43 -0600] "GET /ply/ HTTP/1.1" 304 - +200.55.140.181 - - [28/Feb/2008:14:37:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +200.55.140.181 - - [28/Feb/2008:14:37:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:14:46:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:51:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.37.149.27 - - [28/Feb/2008:14:52:42 -0600] "GET /ply/ HTTP/1.1" 304 - +128.135.11.245 - - [28/Feb/2008:14:55:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.99.44.101 - - [28/Feb/2008:14:57:33 -0600] "GET /robots.txt HTTP/1.0" 200 71 +79.180.31.79 - - [28/Feb/2008:14:58:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.180.31.79 - - [28/Feb/2008:14:58:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +79.180.31.79 - - [28/Feb/2008:14:58:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.232 - - [28/Feb/2008:15:00:11 -0600] "GET /papers/SIAM97/ HTTP/1.0" 403 212 +128.135.11.245 - - [28/Feb/2008:15:04:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:04:46 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +128.135.11.245 - - [28/Feb/2008:15:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:05:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +205.238.131.78 - - [28/Feb/2008:15:05:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ocaml HTTP/1.1" 200 1697 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +205.238.131.78 - - [28/Feb/2008:15:05:15 -0600] "GET /cgi-bin/wiki.pl?OcamlArrayHandling HTTP/1.1" 200 3852 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:15:05:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [28/Feb/2008:15:05:23 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.125.239 - - [28/Feb/2008:15:05:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +205.238.131.78 - - [28/Feb/2008:15:05:50 -0600] "GET /cgi-bin/wiki.pl?OcamlValueExtraction HTTP/1.1" 200 1965 +74.6.20.207 - - [28/Feb/2008:15:06:26 -0600] "GET /writing.html HTTP/1.0" 304 - +205.238.131.78 - - [28/Feb/2008:15:08:34 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +205.238.131.78 - - [28/Feb/2008:15:08:37 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 +67.186.98.20 - - [28/Feb/2008:15:17:40 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [28/Feb/2008:15:18:06 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.24.9 - - [28/Feb/2008:15:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.24.9 - - [28/Feb/2008:15:19:37 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.125.239 - - [28/Feb/2008:15:26:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.125.239 - - [28/Feb/2008:15:26:51 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +128.135.11.245 - - [28/Feb/2008:15:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /index.html HTTP/1.1" 200 4447 +62.59.179.107 - - [28/Feb/2008:15:33:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12562 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [28/Feb/2008:15:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.59.179.107 - - [28/Feb/2008:15:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.15.187.198 - - [28/Feb/2008:15:35:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +68.61.68.187 - - [28/Feb/2008:15:35:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +98.193.69.179 - - [28/Feb/2008:15:39:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:15:39:32 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +24.10.16.193 - - [28/Feb/2008:15:39:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:15:39:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:15:39:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.88.162.35 - - [28/Feb/2008:15:43:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [28/Feb/2008:15:45:54 -0600] "GET /ply/ HTTP/1.0" 304 - +192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET / HTTP/1.1" 200 4447 +192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +24.10.16.193 - - [28/Feb/2008:16:03:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:16:03:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:16:03:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:16:07:22 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [28/Feb/2008:16:07:23 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.55.208.118 - - [28/Feb/2008:16:15:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:15:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.1" 304 - +128.135.125.239 - - [28/Feb/2008:16:16:49 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:18 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.224.122.212 - - [28/Feb/2008:16:17:21 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +82.224.122.212 - - [28/Feb/2008:16:17:24 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +139.82.24.80 - - [28/Feb/2008:16:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:16:20:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +74.6.19.115 - - [28/Feb/2008:16:21:48 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.135.125.239 - - [28/Feb/2008:16:23:01 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:16:27:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.162 - - [28/Feb/2008:16:27:31 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +66.232.113.62 - - [28/Feb/2008:16:28:34 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +85.185.11.131 - - [28/Feb/2008:16:28:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.135.164.162 - - [28/Feb/2008:16:30:06 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 +128.135.164.162 - - [28/Feb/2008:16:30:38 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE025.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE072.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE120.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:16:31:00 -0600] "HEAD /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 0 +128.135.164.162 - - [28/Feb/2008:16:31:09 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +66.146.214.212 - - [28/Feb/2008:16:36:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +66.146.214.212 - - [28/Feb/2008:16:36:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:37:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:16:40:33 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +71.57.91.136 - - [28/Feb/2008:16:40:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +71.57.91.136 - - [28/Feb/2008:16:40:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:16:42:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +66.146.214.212 - - [28/Feb/2008:16:43:10 -0600] "GET /dynamic/ HTTP/1.1" 304 - +80.229.34.140 - - [28/Feb/2008:16:43:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:16:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.99.13.122 - - [28/Feb/2008:16:45:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.173.200.39 - - [28/Feb/2008:16:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.200.39 - - [28/Feb/2008:16:53:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +128.135.11.245 - - [28/Feb/2008:16:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:16:55:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:16:55:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [28/Feb/2008:16:55:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.26 - - [28/Feb/2008:16:55:54 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 +86.101.114.10 - - [28/Feb/2008:16:57:39 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.16 - - [28/Feb/2008:16:58:07 -0600] "GET /ply/ HTTP/1.1" 304 - +210.143.35.13 - - [28/Feb/2008:16:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:16:58:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.145.54.15 - - [28/Feb/2008:17:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +216.145.54.15 - - [28/Feb/2008:17:02:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +216.145.54.15 - - [28/Feb/2008:17:02:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1461 +216.145.54.15 - - [28/Feb/2008:17:02:35 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 +209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +209.203.68.2 - - [28/Feb/2008:17:02:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.203.68.2 - - [28/Feb/2008:17:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +216.145.54.15 - - [28/Feb/2008:17:03:15 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +216.145.54.15 - - [28/Feb/2008:17:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +128.135.164.162 - - [28/Feb/2008:17:08:15 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:17:08:47 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +68.37.149.27 - - [28/Feb/2008:17:10:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +128.135.194.70 - - [28/Feb/2008:17:18:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:17:18:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +82.254.123.7 - - [28/Feb/2008:17:19:22 -0600] "GET /ply/ HTTP/1.1" 304 - +128.135.239.73 - - [28/Feb/2008:17:20:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +128.135.239.73 - - [28/Feb/2008:17:20:46 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.239.73 - - [28/Feb/2008:17:21:01 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.207.228.84 - - [28/Feb/2008:17:22:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET / HTTP/1.1" 200 4447 +68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.37.149.27 - - [28/Feb/2008:17:24:00 -0600] "GET /software.html HTTP/1.1" 200 3163 +68.37.149.27 - - [28/Feb/2008:17:24:27 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +128.135.230.179 - - [28/Feb/2008:17:26:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.179 - - [28/Feb/2008:17:26:35 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.37.149.27 - - [28/Feb/2008:17:26:44 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +68.37.149.27 - - [28/Feb/2008:17:26:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +88.191.19.81 - - [28/Feb/2008:17:27:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET / HTTP/1.1" 200 4447 +128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.164.178 - - [28/Feb/2008:17:28:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.178 - - [28/Feb/2008:17:28:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +202.213.221.97 - - [28/Feb/2008:17:28:55 -0600] "GET /robots.txt? HTTP/1.0" 200 71 +202.213.221.97 - - [28/Feb/2008:17:30:29 -0600] "GET / HTTP/1.0" 200 4447 +75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.42.252.241 - - [28/Feb/2008:17:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.42.252.241 - - [28/Feb/2008:17:31:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.135 - - [28/Feb/2008:17:31:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 +71.201.41.248 - - [28/Feb/2008:17:33:24 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +74.6.20.205 - - [28/Feb/2008:17:35:59 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:41 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.207.228.84 - - [28/Feb/2008:17:38:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.197.57 - - [28/Feb/2008:17:39:41 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +210.9.32.205 - - [28/Feb/2008:17:39:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.9.32.205 - - [28/Feb/2008:17:40:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [28/Feb/2008:17:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.193 - - [28/Feb/2008:17:40:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.193 - - [28/Feb/2008:17:40:25 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +67.195.58.168 - - [28/Feb/2008:17:42:57 -0600] "GET /consulting.html HTTP/1.0" 304 - +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET / HTTP/1.1" 200 4447 +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /python.html HTTP/1.1" 200 18870 +128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.63 - - [28/Feb/2008:17:43:44 -0600] "GET /training.html HTTP/1.1" 200 6154 +128.135.194.63 - - [28/Feb/2008:17:43:47 -0600] "GET /index.html HTTP/1.1" 200 4447 +128.135.194.63 - - [28/Feb/2008:17:43:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.194.63 - - [28/Feb/2008:17:44:03 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +193.47.80.43 - - [28/Feb/2008:17:44:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [28/Feb/2008:17:44:20 -0600] "GET / HTTP/1.1" 200 4447 +67.195.58.188 - - [28/Feb/2008:17:44:27 -0600] "GET /training.html HTTP/1.0" 200 6154 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:45:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 145188 +128.135.194.178 - - [28/Feb/2008:17:45:53 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 327437 +67.195.44.110 - - [28/Feb/2008:17:50:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.230.204 - - [28/Feb/2008:17:51:27 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.197.195 - - [28/Feb/2008:17:53:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.197.195 - - [28/Feb/2008:17:53:44 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.143.136.157 - - [28/Feb/2008:18:11:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:45 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +201.236.226.90 - - [28/Feb/2008:18:14:23 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [28/Feb/2008:18:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:18:25:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.197.195 - - [28/Feb/2008:18:29:16 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.197.195 - - [28/Feb/2008:18:29:24 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic HTTP/1.1" 301 246 +128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.197.57 - - [28/Feb/2008:18:29:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +189.13.67.152 - - [28/Feb/2008:18:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.67.152 - - [28/Feb/2008:18:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.111.4.247 - - [28/Feb/2008:18:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:18:31:24 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +211.127.232.14 - - [28/Feb/2008:18:31:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.135.194.70 - - [28/Feb/2008:18:31:29 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +211.127.232.14 - - [28/Feb/2008:18:31:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +211.127.232.14 - - [28/Feb/2008:18:31:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +211.127.232.14 - - [28/Feb/2008:18:32:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1161 +221.189.180.200 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +211.127.232.14 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 +161.45.160.30 - - [28/Feb/2008:18:33:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.160.30 - - [28/Feb/2008:18:35:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +161.45.160.30 - - [28/Feb/2008:18:35:28 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.160.30 - - [28/Feb/2008:18:35:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +189.13.67.152 - - [28/Feb/2008:18:36:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.13.67.152 - - [28/Feb/2008:18:36:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.58.172 - - [28/Feb/2008:18:42:17 -0600] "GET /index.html HTTP/1.0" 200 4447 +128.135.230.204 - - [28/Feb/2008:18:53:45 -0600] "GET /old/index.html HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:49 -0600] "GET /old/ HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:56 -0600] "GET /old/cource18.html HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:59 -0600] "GET / HTTP/1.1" 200 4447 +128.135.230.204 - - [28/Feb/2008:18:54:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.195.58.177 - - [28/Feb/2008:18:55:27 -0600] "GET /python.html HTTP/1.0" 304 - +61.135.190.17 - - [28/Feb/2008:18:56:59 -0600] "GET /ply/ HTTP/1.1" 304 - +64.124.85.74 - - [28/Feb/2008:18:59:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.74 - - [28/Feb/2008:19:02:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.197.195 - - [28/Feb/2008:19:02:31 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.197.195 - - [28/Feb/2008:19:02:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:19:08:39 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +68.81.244.149 - - [28/Feb/2008:19:12:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.135.230.3 - - [28/Feb/2008:19:15:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.3 - - [28/Feb/2008:19:15:40 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.3 - - [28/Feb/2008:19:15:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.230.3 - - [28/Feb/2008:19:15:49 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.230.161 - - [28/Feb/2008:19:17:07 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.230.161 - - [28/Feb/2008:19:17:37 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +98.215.100.68 - - [28/Feb/2008:19:19:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +98.215.100.68 - - [28/Feb/2008:19:20:12 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +212.56.88.112 - - [28/Feb/2008:19:20:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.108 - - [28/Feb/2008:19:22:54 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.19.115 - - [28/Feb/2008:19:24:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.145 - - [28/Feb/2008:19:24:20 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.224.182.16 - - [28/Feb/2008:19:24:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +212.56.88.112 - - [28/Feb/2008:19:26:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +161.45.160.30 - - [28/Feb/2008:19:26:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.114 - - [28/Feb/2008:19:26:57 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +161.45.162.52 - - [28/Feb/2008:19:26:58 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +213.224.182.16 - - [28/Feb/2008:19:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [28/Feb/2008:19:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.165 - - [28/Feb/2008:19:32:19 -0600] "GET /writing.html HTTP/1.0" 200 2871 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.83.111.28 - - [28/Feb/2008:19:33:34 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.194.70 - - [28/Feb/2008:19:34:23 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +161.45.162.52 - - [28/Feb/2008:19:38:50 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +202.213.221.97 - - [28/Feb/2008:19:39:18 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +210.9.32.205 - - [28/Feb/2008:19:39:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.56.88.112 - - [28/Feb/2008:19:40:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.195.34.111 - - [28/Feb/2008:19:44:14 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.111 - - [28/Feb/2008:19:44:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.195.58.158 - - [28/Feb/2008:19:45:40 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +134.159.131.34 - - [28/Feb/2008:19:45:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.159.131.34 - - [28/Feb/2008:19:45:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.159.131.34 - - [28/Feb/2008:19:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.195.58.181 - - [28/Feb/2008:19:50:24 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +67.195.58.151 - - [28/Feb/2008:19:50:37 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +67.195.58.170 - - [28/Feb/2008:19:50:56 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +122.152.129.53 - - [28/Feb/2008:19:51:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +212.56.88.112 - - [28/Feb/2008:19:52:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:16 -0600] "GET /dynamic/lecture8 HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:21 -0600] "GET /dynamic/lecture8/ HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:30 -0600] "GET /dynamic/Lecture8/ HTTP/1.1" 404 133 +128.135.230.179 - - [28/Feb/2008:20:03:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.179 - - [28/Feb/2008:20:03:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +212.56.88.112 - - [28/Feb/2008:20:15:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +66.232.113.194 - - [28/Feb/2008:20:22:08 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +213.227.137.187 - - [28/Feb/2008:20:22:09 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [28/Feb/2008:20:22:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +195.50.147.66 - - [28/Feb/2008:20:25:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.50.147.66 - - [28/Feb/2008:20:25:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [28/Feb/2008:20:27:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [28/Feb/2008:20:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.152.140.206 - - [28/Feb/2008:20:32:56 -0600] "GET / HTTP/1.1" 200 4447 +201.16.201.21 - - [28/Feb/2008:20:38:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.16.201.21 - - [28/Feb/2008:20:38:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.154 - - [28/Feb/2008:20:40:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.164.154 - - [28/Feb/2008:20:40:33 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.135.164.154 - - [28/Feb/2008:20:42:02 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +204.246.129.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.46.48.194 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +202.44.78.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +202.44.78.196 - - [28/Feb/2008:20:43:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +202.44.78.196 - - [28/Feb/2008:20:43:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +206.51.237.114 - - [28/Feb/2008:20:44:26 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +80.191.131.2 - - [28/Feb/2008:20:44:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.185.116.11 - - [28/Feb/2008:20:44:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.135.164.154 - - [28/Feb/2008:20:46:24 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 304 - +82.189.46.148 - - [28/Feb/2008:20:46:59 -0600] "GET / HTTP/1.0" 200 4447 +82.189.46.148 - - [28/Feb/2008:20:47:00 -0600] "GET /about.html HTTP/1.0" 200 7890 +82.189.46.148 - - [28/Feb/2008:20:47:10 -0600] "GET /training.html HTTP/1.0" 200 6154 +82.189.46.148 - - [28/Feb/2008:20:47:19 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +82.189.46.148 - - [28/Feb/2008:20:47:21 -0600] "GET /python.html HTTP/1.0" 200 18870 +82.189.46.148 - - [28/Feb/2008:20:47:23 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +82.189.46.148 - - [28/Feb/2008:20:47:28 -0600] "GET /writing.html HTTP/1.0" 200 2871 +82.189.46.148 - - [28/Feb/2008:20:47:29 -0600] "GET /software.html HTTP/1.0" 200 3163 +82.189.46.148 - - [28/Feb/2008:20:47:30 -0600] "GET /index.html HTTP/1.0" 200 4447 +82.189.46.148 - - [28/Feb/2008:20:47:32 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +82.189.46.148 - - [28/Feb/2008:20:47:35 -0600] "GET /cv.html HTTP/1.0" 200 31798 +82.189.46.148 - - [28/Feb/2008:20:47:38 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +82.189.46.148 - - [28/Feb/2008:20:47:40 -0600] "GET /ply/README HTTP/1.0" 200 8605 +82.189.46.148 - - [28/Feb/2008:20:47:42 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +82.189.46.148 - - [28/Feb/2008:20:47:43 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +82.189.46.148 - - [28/Feb/2008:20:47:45 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 +67.195.44.110 - - [28/Feb/2008:20:49:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [28/Feb/2008:20:49:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.28.237 - - [28/Feb/2008:20:50:01 -0600] "GET /photos/wind/pages/IMG_1309.htm HTTP/1.0" 404 133 +128.135.164.154 - - [28/Feb/2008:20:50:18 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +128.135.164.154 - - [28/Feb/2008:20:50:59 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +74.6.25.125 - - [28/Feb/2008:20:51:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 +128.135.164.154 - - [28/Feb/2008:20:55:56 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +219.142.125.240 - - [28/Feb/2008:20:55:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +219.142.125.240 - - [28/Feb/2008:20:56:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +219.142.125.240 - - [28/Feb/2008:20:56:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +201.16.201.21 - - [28/Feb/2008:20:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.154 - - [28/Feb/2008:20:59:38 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - +128.135.164.154 - - [28/Feb/2008:20:59:44 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 206 416530 +219.142.125.240 - - [28/Feb/2008:21:01:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +65.55.208.117 - - [28/Feb/2008:21:03:24 -0600] "GET /robots.txt HTTP/1.1" 200 71 +217.196.43.134 - - [28/Feb/2008:21:05:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.217.219.85 - - [28/Feb/2008:21:17:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.52 - - [28/Feb/2008:21:18:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [28/Feb/2008:21:18:33 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 +77.91.224.3 - - [28/Feb/2008:21:20:30 -0600] "GET /robots.txt HTTP/1.1" 200 71 +71.57.91.136 - - [28/Feb/2008:21:20:30 -0600] "GET /dynamic/ HTTP/1.1" 304 - +77.91.224.3 - - [28/Feb/2008:21:20:31 -0600] "GET / HTTP/1.1" 200 4447 +77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +211.127.232.14 - - [28/Feb/2008:21:22:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +84.110.148.125 - - [28/Feb/2008:21:23:39 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [28/Feb/2008:21:23:40 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.148.125 - - [28/Feb/2008:21:23:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +84.110.187.74 - - [28/Feb/2008:21:23:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +24.37.1.68 - - [28/Feb/2008:21:23:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.37.1.68 - - [28/Feb/2008:21:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.37.1.68 - - [28/Feb/2008:21:23:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +211.127.232.14 - - [28/Feb/2008:21:25:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +67.176.197.254 - - [28/Feb/2008:21:27:07 -0600] "GET / HTTP/1.1" 200 4447 +207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /cgi-bin/wiki.pl?S HTTP/1.1" 200 1163 +207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:27:12 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.176.197.254 - - [28/Feb/2008:21:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:27:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.47.98.129 - - [28/Feb/2008:21:27:28 -0600] "GET /cgi-bin/wiki.pl? HTTP/1.1" 200 2883 +207.47.98.129 - - [28/Feb/2008:21:27:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +211.127.232.14 - - [28/Feb/2008:21:27:40 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 +207.47.98.129 - - [28/Feb/2008:21:27:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 +211.127.232.14 - - [28/Feb/2008:21:27:48 -0600] "GET /cgi-bin/wiki.pl?action=history&id=SwigFaqDLLUsingMingw HTTP/1.1" 200 2787 +207.47.98.129 - - [28/Feb/2008:21:28:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +207.47.98.129 - - [28/Feb/2008:21:28:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2665 +71.57.91.136 - - [28/Feb/2008:21:28:56 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +207.47.98.129 - - [28/Feb/2008:21:28:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +71.57.91.136 - - [28/Feb/2008:21:28:57 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +70.41.192.171 - - [28/Feb/2008:21:34:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:39:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +67.195.58.164 - - [28/Feb/2008:21:43:01 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 +193.252.149.16 - - [28/Feb/2008:21:44:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [28/Feb/2008:21:44:07 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 +84.110.177.245 - - [28/Feb/2008:21:45:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.245 - - [28/Feb/2008:21:45:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 981 +70.41.192.171 - - [28/Feb/2008:21:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [28/Feb/2008:22:03:29 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +83.238.23.214 - - [28/Feb/2008:22:03:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +84.110.153.190 - - [28/Feb/2008:22:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.153.190 - - [28/Feb/2008:22:05:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1205 +24.12.12.183 - - [28/Feb/2008:22:08:26 -0600] "GET / HTTP/1.1" 200 4447 +24.12.12.183 - - [28/Feb/2008:22:08:27 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +24.12.12.183 - - [28/Feb/2008:22:08:29 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +24.12.12.183 - - [28/Feb/2008:22:08:33 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +64.135.175.82 - - [28/Feb/2008:22:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +64.135.175.82 - - [28/Feb/2008:22:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.135.175.82 - - [28/Feb/2008:22:09:54 -0600] "GET /cgi-bin/wiki.pl?back=/SharedLibraries HTTP/1.1" 200 1111 +210.245.31.3 - - [28/Feb/2008:22:10:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.245.31.3 - - [28/Feb/2008:22:10:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.245.31.3 - - [28/Feb/2008:22:10:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.135.175.82 - - [28/Feb/2008:22:11:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +24.12.12.183 - - [28/Feb/2008:22:11:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +70.41.192.171 - - [28/Feb/2008:22:19:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.24.133.241 - - [28/Feb/2008:22:20:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.24.133.241 - - [28/Feb/2008:22:20:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.24.112 - - [28/Feb/2008:22:26:05 -0600] "GET /diversions.html HTTP/1.0" 200 2427 +67.195.58.186 - - [28/Feb/2008:22:32:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +67.176.197.254 - - [28/Feb/2008:22:35:53 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.176.197.254 - - [28/Feb/2008:22:35:54 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:22:41:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +131.215.42.190 - - [28/Feb/2008:22:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:22:46:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:49:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [28/Feb/2008:22:53:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +24.1.247.118 - - [28/Feb/2008:22:53:47 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +216.19.182.199 - - [28/Feb/2008:22:54:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:57:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.195.58.164 - - [28/Feb/2008:23:02:55 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:08:43 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +68.37.149.27 - - [28/Feb/2008:23:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 206 96799 +68.37.149.27 - - [28/Feb/2008:23:13:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.10.16.193 - - [28/Feb/2008:23:19:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [28/Feb/2008:23:22:45 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +131.215.42.190 - - [28/Feb/2008:23:24:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +131.215.42.190 - - [28/Feb/2008:23:24:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.37.149.27 - - [28/Feb/2008:23:28:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +131.215.42.190 - - [28/Feb/2008:23:29:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 +131.215.42.190 - - [28/Feb/2008:23:32:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.132.65.31 - - [28/Feb/2008:23:34:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.132.65.31 - - [28/Feb/2008:23:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.132.65.31 - - [28/Feb/2008:23:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.37.149.27 - - [28/Feb/2008:23:35:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +131.215.42.190 - - [28/Feb/2008:23:36:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.182 - - [28/Feb/2008:23:37:18 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +24.1.159.241 - - [28/Feb/2008:23:42:48 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 339721 +131.215.42.190 - - [28/Feb/2008:23:43:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:23:43:20 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1145 +131.215.42.190 - - [28/Feb/2008:23:43:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +131.215.42.190 - - [28/Feb/2008:23:43:28 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +131.215.42.190 - - [28/Feb/2008:23:43:32 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1152 +131.215.42.190 - - [28/Feb/2008:23:43:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +131.215.42.190 - - [28/Feb/2008:23:43:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +131.215.42.190 - - [28/Feb/2008:23:43:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 15264 +203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.20.35.28 - - [28/Feb/2008:23:55:55 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 206 188184 +61.14.187.142 - - [29/Feb/2008:00:04:15 -0600] "GET / HTTP/1.0" 200 4447 +61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /python.html HTTP/1.0" 200 18870 +61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /index.html HTTP/1.0" 200 4447 +61.14.187.142 - - [29/Feb/2008:00:04:17 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 +61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /writing.html HTTP/1.0" 200 2871 +61.14.187.142 - - [29/Feb/2008:00:04:19 -0600] "GET /about.html HTTP/1.0" 200 7890 +61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /software.html HTTP/1.0" 200 3163 +61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /training.html HTTP/1.0" 200 6154 +61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 +217.172.44.82 - - [29/Feb/2008:00:04:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.14.187.142 - - [29/Feb/2008:00:04:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /ply/README HTTP/1.0" 200 8605 +61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +61.14.187.142 - - [29/Feb/2008:00:04:25 -0600] "GET /publications.html HTTP/1.0" 200 7758 +61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 +61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 +61.14.187.142 - - [29/Feb/2008:00:04:28 -0600] "GET /papers/Py96/python96.html HTTP/1.0" 200 22442 +61.14.187.142 - - [29/Feb/2008:00:04:29 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +67.97.80.5 - - [29/Feb/2008:00:16:24 -0600] "GET /python.html HTTP/1.0" 200 18870 +67.97.80.5 - - [29/Feb/2008:00:16:25 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /python.html HTTP/1.0" 304 - +67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 304 - +67.97.80.5 - - [29/Feb/2008:00:17:27 -0600] "GET /training.html HTTP/1.0" 200 6154 +213.145.165.82 - - [29/Feb/2008:00:17:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.48.187.205 - - [29/Feb/2008:00:24:52 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +125.19.42.35 - - [29/Feb/2008:00:26:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 +125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.19.42.35 - - [29/Feb/2008:00:29:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [29/Feb/2008:00:32:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +125.16.133.35 - - [29/Feb/2008:00:32:10 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +125.19.42.35 - - [29/Feb/2008:00:32:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [29/Feb/2008:00:33:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [29/Feb/2008:00:33:36 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /python.html HTTP/1.1" 304 - +125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - +67.186.98.20 - - [29/Feb/2008:00:54:58 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.100 - - [29/Feb/2008:00:58:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 +74.6.29.23 - - [29/Feb/2008:01:15:57 -0600] "GET /dynamic/ HTTP/1.0" 200 5653 +61.57.149.13 - - [29/Feb/2008:01:17:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +61.57.149.13 - - [29/Feb/2008:01:17:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.149.13 - - [29/Feb/2008:01:17:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.149.13 - - [29/Feb/2008:01:18:00 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 +74.6.28.122 - - [29/Feb/2008:01:22:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.0" 200 343614 +193.252.149.16 - - [29/Feb/2008:01:24:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [29/Feb/2008:01:24:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +78.99.69.34 - - [29/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +78.99.69.34 - - [29/Feb/2008:01:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE003.HTM HTTP/1.1" 304 - +38.98.120.84 - - [29/Feb/2008:01:49:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [29/Feb/2008:01:49:22 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [29/Feb/2008:01:49:23 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [29/Feb/2008:01:49:24 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [29/Feb/2008:01:49:25 -0600] "GET /sitemap.html HTTP/1.1" 404 133 +38.98.120.84 - - [29/Feb/2008:01:49:27 -0600] "GET / HTTP/1.1" 200 4447 +80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.239.94.225 - - [29/Feb/2008:02:02:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.239.94.225 - - [29/Feb/2008:02:02:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.112.65.218 - - [29/Feb/2008:02:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +91.112.65.218 - - [29/Feb/2008:02:03:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +91.112.65.218 - - [29/Feb/2008:02:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.112.65.218 - - [29/Feb/2008:02:03:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +91.112.65.218 - - [29/Feb/2008:02:04:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.128.18.187 - - [29/Feb/2008:02:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.128.18.187 - - [29/Feb/2008:02:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /photos/wind/pages/IMG_1267.htm HTTP/1.1" 404 133 +91.49.96.242 - - [29/Feb/2008:02:12:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.75.252.165 - - [29/Feb/2008:02:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +210.75.252.165 - - [29/Feb/2008:02:14:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.75.252.165 - - [29/Feb/2008:02:14:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [29/Feb/2008:02:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +210.75.252.165 - - [29/Feb/2008:02:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.112.65.218 - - [29/Feb/2008:02:26:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +67.195.58.174 - - [29/Feb/2008:02:28:27 -0600] "GET /ply/ HTTP/1.0" 304 - +91.112.65.218 - - [29/Feb/2008:02:28:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +67.195.58.160 - - [29/Feb/2008:02:29:13 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +67.195.58.178 - - [29/Feb/2008:02:29:25 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +85.97.128.179 - - [29/Feb/2008:02:36:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +85.97.128.179 - - [29/Feb/2008:02:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.97.128.179 - - [29/Feb/2008:02:37:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.97.128.179 - - [29/Feb/2008:02:37:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.118 - - [29/Feb/2008:02:39:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [29/Feb/2008:02:39:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 200 1337 +67.195.58.168 - - [29/Feb/2008:02:43:46 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +67.195.58.175 - - [29/Feb/2008:02:49:18 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +221.239.94.225 - - [29/Feb/2008:02:53:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 9416 +221.239.94.225 - - [29/Feb/2008:02:53:11 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 42184 +221.239.94.225 - - [29/Feb/2008:02:53:12 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 74952 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 29862 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 +221.239.94.225 - - [29/Feb/2008:02:53:15 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 +121.14.96.153 - - [29/Feb/2008:02:53:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.60.14.236 - - [29/Feb/2008:02:53:19 -0600] "GET / HTTP/1.1" 200 4447 +124.115.1.67 - - [29/Feb/2008:02:53:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +121.14.96.152 - - [29/Feb/2008:02:53:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 +58.60.13.231 - - [29/Feb/2008:02:53:32 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +202.58.71.138 - - [29/Feb/2008:02:54:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +202.58.71.138 - - [29/Feb/2008:02:54:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.58.71.138 - - [29/Feb/2008:02:58:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 +202.58.71.138 - - [29/Feb/2008:02:59:02 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +202.58.71.138 - - [29/Feb/2008:02:59:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +210.217.95.253 - - [29/Feb/2008:03:00:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +210.217.95.253 - - [29/Feb/2008:03:00:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +82.127.117.160 - - [29/Feb/2008:03:05:32 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.127.117.160 - - [29/Feb/2008:03:05:33 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.127.117.160 - - [29/Feb/2008:03:05:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.110.143.135 - - [29/Feb/2008:03:08:58 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.143.135 - - [29/Feb/2008:03:09:08 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +210.51.195.13 - - [29/Feb/2008:03:13:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +210.51.195.13 - - [29/Feb/2008:03:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +210.51.195.13 - - [29/Feb/2008:03:14:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +210.51.195.13 - - [29/Feb/2008:03:14:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +60.28.17.44 - - [29/Feb/2008:03:15:03 -0600] "GET / HTTP/1.1" 200 4447 +210.51.195.13 - - [29/Feb/2008:03:19:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +220.220.204.13 - - [29/Feb/2008:03:21:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +220.220.204.13 - - [29/Feb/2008:03:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.220.204.13 - - [29/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.220.204.13 - - [29/Feb/2008:03:21:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/ HTTP/1.1" 304 - +221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +220.220.204.13 - - [29/Feb/2008:03:21:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1323 +220.220.204.13 - - [29/Feb/2008:03:21:52 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 +220.220.204.13 - - [29/Feb/2008:03:21:57 -0600] "GET /cgi-bin/wiki.pl?AddmethodsDirective HTTP/1.1" 200 1671 +220.220.204.13 - - [29/Feb/2008:03:22:04 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +67.195.58.178 - - [29/Feb/2008:03:29:56 -0600] "GET /software.html HTTP/1.0" 304 - +67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [29/Feb/2008:03:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.99.30.64 - - [29/Feb/2008:03:32:41 -0600] "GET /robots.txt HTTP/1.0" 200 71 +91.103.40.50 - - [29/Feb/2008:03:34:38 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +80.58.205.45 - - [29/Feb/2008:03:36:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.249.147.120 - - [29/Feb/2008:03:36:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.249.147.120 - - [29/Feb/2008:03:36:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.249.147.120 - - [29/Feb/2008:03:36:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:38:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:40:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:47:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [29/Feb/2008:03:48:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.10.60.85 - - [29/Feb/2008:03:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +194.2.41.91 - - [29/Feb/2008:03:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +117.47.114.193 - - [29/Feb/2008:03:59:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +117.47.114.193 - - [29/Feb/2008:03:59:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.2.41.91 - - [29/Feb/2008:03:59:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +218.186.13.1 - - [29/Feb/2008:04:03:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.1" 200 1352 +218.186.13.1 - - [29/Feb/2008:04:03:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [29/Feb/2008:04:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.232.113.62 - - [29/Feb/2008:04:06:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +202.183.216.180 - - [29/Feb/2008:04:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.62.170.203 - - [29/Feb/2008:04:06:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.227.137.187 - - [29/Feb/2008:04:06:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.227.1.101 - - [29/Feb/2008:04:07:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +74.6.25.70 - - [29/Feb/2008:04:20:50 -0600] "GET /gifplot/index.html HTTP/1.0" 200 40215 +74.6.26.75 - - [29/Feb/2008:04:23:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12706 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.206.186.101 - - [29/Feb/2008:04:30:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.206.186.101 - - [29/Feb/2008:04:32:37 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.19.72 - - [29/Feb/2008:04:39:31 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 +74.6.23.12 - - [29/Feb/2008:04:43:13 -0600] "GET /dynamic/dowstocks.csv HTTP/1.0" 200 589814 +206.51.226.87 - - [29/Feb/2008:04:43:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +218.64.214.110 - - [29/Feb/2008:04:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.195.243.236 - - [29/Feb/2008:04:43:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +196.217.249.190 - - [29/Feb/2008:04:43:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +203.199.177.121 - - [29/Feb/2008:04:43:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [29/Feb/2008:04:43:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +87.18.28.170 - - [29/Feb/2008:04:44:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.232.219.113 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.219.113 - - [29/Feb/2008:04:44:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.219.113 - - [29/Feb/2008:04:44:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.217.150.45 - - [29/Feb/2008:04:46:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.90.215.85 - - [29/Feb/2008:04:48:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 +193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/ HTTP/1.0" 304 - +193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +193.0.96.15 - - [29/Feb/2008:04:51:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 +81.80.245.157 - - [29/Feb/2008:04:53:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [29/Feb/2008:04:54:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.165.112.79 - - [29/Feb/2008:04:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.165.112.79 - - [29/Feb/2008:04:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.125.158.237 - - [29/Feb/2008:04:58:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.7.146 - - [29/Feb/2008:04:58:16 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +74.6.25.23 - - [29/Feb/2008:05:03:37 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1459 +74.6.20.166 - - [29/Feb/2008:05:11:50 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +67.195.58.174 - - [29/Feb/2008:05:20:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +74.6.26.209 - - [29/Feb/2008:05:23:06 -0600] "GET /python/consulting.html HTTP/1.0" 404 133 +68.37.149.27 - - [29/Feb/2008:05:24:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.165.73.226 - - [29/Feb/2008:05:32:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.165.73.226 - - [29/Feb/2008:05:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.226 - - [29/Feb/2008:05:33:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.226 - - [29/Feb/2008:05:33:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +91.103.40.50 - - [29/Feb/2008:05:42:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +210.197.158.144 - - [29/Feb/2008:05:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.127.12.71 - - [29/Feb/2008:06:10:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.127.12.71 - - [29/Feb/2008:06:10:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.127.12.71 - - [29/Feb/2008:06:10:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.127.12.71 - - [29/Feb/2008:06:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [29/Feb/2008:06:20:12 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.211.198.146 - - [29/Feb/2008:06:20:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.211.198.146 - - [29/Feb/2008:06:20:27 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +81.255.174.7 - - [29/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.160.169.7 - - [29/Feb/2008:06:24:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +81.255.174.7 - - [29/Feb/2008:06:24:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.128.46.55 - - [29/Feb/2008:06:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +217.128.46.55 - - [29/Feb/2008:06:27:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET / HTTP/1.1" 200 4447 +74.6.25.20 - - [29/Feb/2008:06:34:27 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.169 - - [29/Feb/2008:06:34:42 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.246.151.62 - - [29/Feb/2008:06:38:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:06:43:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.166 - - [29/Feb/2008:06:44:56 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 304 - +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 304 - +200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +200.19.92.10 - - [29/Feb/2008:06:58:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +131.111.113.139 - - [29/Feb/2008:07:03:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +131.111.113.139 - - [29/Feb/2008:07:03:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +190.198.190.239 - - [29/Feb/2008:07:04:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.198.190.239 - - [29/Feb/2008:07:05:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.249.65.37 - - [29/Feb/2008:07:05:18 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:06:23 -0600] "GET /dynamic/soln1.html HTTP/1.1" 304 - +80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:07:11:32 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:13:19 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +66.249.65.37 - - [29/Feb/2008:07:14:41 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 304 - +66.232.113.194 - - [29/Feb/2008:07:19:49 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +80.227.1.101 - - [29/Feb/2008:07:19:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +201.25.119.178 - - [29/Feb/2008:07:20:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.25.119.178 - - [29/Feb/2008:07:20:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +220.225.196.123 - - [29/Feb/2008:07:20:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +202.97.149.167 - - [29/Feb/2008:07:20:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +87.249.53.100 - - [29/Feb/2008:07:20:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +206.51.237.114 - - [29/Feb/2008:07:23:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +123.190.193.8 - - [29/Feb/2008:07:23:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +194.177.202.247 - - [29/Feb/2008:07:23:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +66.249.65.37 - - [29/Feb/2008:07:23:52 -0600] "GET /dynamic/assign2.html HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:25:09 -0600] "GET /python/python.html HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:07:27:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +66.249.65.37 - - [29/Feb/2008:07:27:56 -0600] "GET /dynamic/assign3.html HTTP/1.1" 304 - +200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.19.92.58 - - [29/Feb/2008:07:31:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +66.249.65.37 - - [29/Feb/2008:07:31:24 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 304 - +81.222.64.10 - - [29/Feb/2008:07:31:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.26.11 - - [29/Feb/2008:07:31:48 -0600] "GET /photos/wind/pages/IMG_1321.htm HTTP/1.0" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:52 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.161.85.77 - - [29/Feb/2008:07:44:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:47:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:52:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 new file mode 100644 index 0000000..805c9eb Binary files /dev/null and b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0108.bz2 differ diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0208.bz2 b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0208.bz2 new file mode 100644 index 0000000..805c9eb Binary files /dev/null and b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/bar/access-log-0208.bz2 differ diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log new file mode 100644 index 0000000..deeb937 --- /dev/null +++ b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log @@ -0,0 +1,7298 @@ +140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.180.132.213 - - [24/Feb/2008:00:08:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.54.118.139 - - [24/Feb/2008:00:15:40 -0600] "GET / HTTP/1.1" 200 4447 +75.54.118.139 - - [24/Feb/2008:00:15:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +75.54.118.139 - - [24/Feb/2008:00:15:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.54.118.139 - - [24/Feb/2008:00:15:49 -0600] "GET /software.html HTTP/1.1" 200 3163 +75.54.118.139 - - [24/Feb/2008:00:16:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +75.54.118.139 - - [24/Feb/2008:00:16:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.145.165.82 - - [24/Feb/2008:00:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.83 - - [24/Feb/2008:00:31:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.83 - - [24/Feb/2008:00:31:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +86.132.71.214 - - [24/Feb/2008:00:37:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.132.71.214 - - [24/Feb/2008:00:38:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.144 - - [24/Feb/2008:00:48:16 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 3110734 +74.6.7.122 - - [24/Feb/2008:00:56:36 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1095 +125.25.238.64 - - [24/Feb/2008:01:04:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +125.25.238.64 - - [24/Feb/2008:01:04:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12382 +116.94.207.182 - - [24/Feb/2008:01:09:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +116.94.207.182 - - [24/Feb/2008:01:10:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +116.94.207.182 - - [24/Feb/2008:01:10:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.136.157 - - [24/Feb/2008:01:13:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.230 - - [24/Feb/2008:01:33:09 -0600] "GET /photos/u505/pages/IMG_1508.htm HTTP/1.0" 404 133 +128.143.38.83 - - [24/Feb/2008:01:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +198.37.27.153 - - [24/Feb/2008:01:36:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.83 - - [24/Feb/2008:01:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.179.114.61 - - [24/Feb/2008:01:55:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.179.114.61 - - [24/Feb/2008:01:56:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.8.73 - - [24/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +217.136.207.156 - - [24/Feb/2008:02:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.136.207.156 - - [24/Feb/2008:02:08:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.136.207.156 - - [24/Feb/2008:02:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:11 -0600] "GET /ply/example.html HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:15:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.136.207.156 - - [24/Feb/2008:02:15:20 -0600] "HEAD /ply/PLYTalk.pdf HTTP/1.1" 200 0 +217.136.207.156 - - [24/Feb/2008:02:15:40 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +77.81.4.30 - - [24/Feb/2008:02:17:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [24/Feb/2008:02:20:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [24/Feb/2008:02:22:08 -0600] "GET /ply/example.html HTTP/1.1" 304 - +89.182.136.236 - - [24/Feb/2008:02:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:02:23:29 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +117.198.144.124 - - [24/Feb/2008:02:23:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +117.198.144.124 - - [24/Feb/2008:02:23:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:02:24:41 -0600] "GET / HTTP/1.1" 304 - +66.249.65.37 - - [24/Feb/2008:02:26:21 -0600] "GET /index.html HTTP/1.1" 304 - +66.249.65.37 - - [24/Feb/2008:02:26:54 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.232.113.62 - - [24/Feb/2008:02:29:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +200.133.15.2 - - [24/Feb/2008:02:29:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.165.49.150 - - [24/Feb/2008:02:29:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.165.49.150 - - [24/Feb/2008:02:30:00 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1374 +75.165.49.150 - - [24/Feb/2008:02:30:06 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 +74.6.19.156 - - [24/Feb/2008:02:30:21 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 +77.81.4.30 - - [24/Feb/2008:02:34:50 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.8.73 - - [24/Feb/2008:02:39:13 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +189.141.19.88 - - [24/Feb/2008:02:49:01 -0600] "GET /ply/ HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:02:49:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:02:49:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.249.65.37 - - [24/Feb/2008:02:53:59 -0600] "GET /python.html HTTP/1.1" 304 - +131.107.0.112 - - [24/Feb/2008:03:02:22 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +217.196.43.134 - - [24/Feb/2008:03:05:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.100.2.3 - - [24/Feb/2008:03:05:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.100.2.3 - - [24/Feb/2008:03:05:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.8.73 - - [24/Feb/2008:03:09:17 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +88.179.52.81 - - [24/Feb/2008:03:14:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.179.52.81 - - [24/Feb/2008:03:14:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.179.52.81 - - [24/Feb/2008:03:14:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.8.73 - - [24/Feb/2008:03:34:07 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +64.81.229.55 - - [24/Feb/2008:03:49:00 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +64.81.229.55 - - [24/Feb/2008:03:49:08 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +64.81.229.55 - - [24/Feb/2008:03:49:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +58.120.219.129 - - [24/Feb/2008:04:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.120.219.129 - - [24/Feb/2008:04:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.120.219.129 - - [24/Feb/2008:04:03:01 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +58.120.219.129 - - [24/Feb/2008:04:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +65.55.208.119 - - [24/Feb/2008:04:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [24/Feb/2008:04:04:59 -0600] "GET /about.html HTTP/1.1" 200 7890 +74.6.25.20 - - [24/Feb/2008:04:19:14 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.24.37 - - [24/Feb/2008:04:19:14 -0600] "GET /photos/wind/pages/IMG_1255.htm HTTP/1.0" 404 133 +65.55.208.123 - - [24/Feb/2008:04:27:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.123 - - [24/Feb/2008:04:27:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.1" 304 - +86.129.156.19 - - [24/Feb/2008:04:31:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +83.8.193.46 - - [24/Feb/2008:04:31:58 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.8.193.46 - - [24/Feb/2008:04:32:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.66.109.148 - - [24/Feb/2008:04:37:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.53.254.51 - - [24/Feb/2008:04:37:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +79.66.109.148 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.53.254.51 - - [24/Feb/2008:04:37:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.66.109.148 - - [24/Feb/2008:04:38:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +79.66.109.148 - - [24/Feb/2008:04:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +79.66.109.148 - - [24/Feb/2008:04:38:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +83.53.254.51 - - [24/Feb/2008:04:39:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.230.94.215 - - [24/Feb/2008:04:45:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +61.230.94.215 - - [24/Feb/2008:04:45:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:45:55 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +61.230.94.215 - - [24/Feb/2008:04:47:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.230.94.215 - - [24/Feb/2008:04:47:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +61.230.94.215 - - [24/Feb/2008:04:48:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +61.230.94.215 - - [24/Feb/2008:04:51:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.241.141.161 - - [24/Feb/2008:05:01:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.241.141.161 - - [24/Feb/2008:05:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.241.141.161 - - [24/Feb/2008:05:01:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.232.113.194 - - [24/Feb/2008:05:01:44 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +80.227.1.100 - - [24/Feb/2008:05:01:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +58.176.3.7 - - [24/Feb/2008:05:01:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +60.28.31.194 - - [24/Feb/2008:05:02:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +172.207.178.122 - - [24/Feb/2008:05:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +172.207.178.122 - - [24/Feb/2008:05:05:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.99.116.176 - - [24/Feb/2008:05:07:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.99.116.176 - - [24/Feb/2008:05:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [24/Feb/2008:05:28:58 -0600] "GET /ply/ HTTP/1.0" 304 - +86.157.119.197 - - [24/Feb/2008:05:35:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.202.49.172 - - [24/Feb/2008:05:37:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [24/Feb/2008:05:37:27 -0600] "GET / HTTP/1.1" 200 4447 +66.116.72.114 - - [24/Feb/2008:05:40:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:05:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [24/Feb/2008:05:42:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 +206.51.237.114 - - [24/Feb/2008:05:49:52 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +200.65.127.161 - - [24/Feb/2008:05:49:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +145.76.18.22 - - [24/Feb/2008:05:49:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +222.122.236.43 - - [24/Feb/2008:05:51:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +137.138.64.218 - - [24/Feb/2008:05:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.44.107 - - [24/Feb/2008:06:00:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +86.157.119.197 - - [24/Feb/2008:06:00:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.8.73 - - [24/Feb/2008:06:14:20 -0600] "GET /training.html HTTP/1.0" 200 6154 +74.6.27.115 - - [24/Feb/2008:06:15:19 -0600] "GET /photos/wind/pages/IMG_1327.htm HTTP/1.0" 404 133 +72.30.226.134 - - [24/Feb/2008:06:20:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +84.110.188.195 - - [24/Feb/2008:06:23:38 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.188.195 - - [24/Feb/2008:06:23:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +67.195.44.109 - - [24/Feb/2008:06:27:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 +61.135.219.5 - - [24/Feb/2008:06:28:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +74.6.8.73 - - [24/Feb/2008:06:32:05 -0600] "GET /photos/wind/pages/IMG_1277.htm HTTP/1.0" 404 133 +84.110.187.127 - - [24/Feb/2008:06:52:46 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.127 - - [24/Feb/2008:06:52:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +66.249.65.37 - - [24/Feb/2008:06:56:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.1" 200 1279 +66.249.65.37 - - [24/Feb/2008:06:57:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.1" 200 1231 +66.249.65.37 - - [24/Feb/2008:07:01:40 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE092.HTM HTTP/1.1" 200 1329 +65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.104.13 - - [24/Feb/2008:07:03:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.249.65.37 - - [24/Feb/2008:07:03:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.1" 200 1322 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.49.130.55 - - [24/Feb/2008:07:04:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12238 +89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.49.130.55 - - [24/Feb/2008:07:04:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.49.130.55 - - [24/Feb/2008:07:04:15 -0600] "GET /ply/README HTTP/1.1" 200 8605 +125.99.164.76 - - [24/Feb/2008:07:11:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.249.65.37 - - [24/Feb/2008:07:14:19 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE050.HTM HTTP/1.1" 200 982 +84.110.206.219 - - [24/Feb/2008:07:15:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.206.219 - - [24/Feb/2008:07:15:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1047 +84.110.117.89 - - [24/Feb/2008:07:15:34 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.117.89 - - [24/Feb/2008:07:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1101 +84.110.189.175 - - [24/Feb/2008:07:15:55 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.175 - - [24/Feb/2008:07:15:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1158 +89.49.130.55 - - [24/Feb/2008:07:20:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.110.121.137 - - [24/Feb/2008:07:36:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.121.137 - - [24/Feb/2008:07:36:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1107 +80.93.56.177 - - [24/Feb/2008:07:57:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.93.56.116 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:57:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.177 - - [24/Feb/2008:07:57:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.93.56.116 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.177 - - [24/Feb/2008:07:58:00 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.116 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 +80.93.56.177 - - [24/Feb/2008:07:58:01 -0600] "GET / HTTP/1.1" 200 4447 +66.232.113.62 - - [24/Feb/2008:08:03:16 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2754 +83.229.21.4 - - [24/Feb/2008:08:03:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +219.136.206.29 - - [24/Feb/2008:08:03:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +83.8.193.46 - - [24/Feb/2008:08:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.196.97.157 - - [24/Feb/2008:08:20:04 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:05 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 +66.196.97.157 - - [24/Feb/2008:08:20:07 -0600] "GET / HTTP/1.0" 200 4447 +79.182.112.231 - - [24/Feb/2008:08:27:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.226.58.44 - - [24/Feb/2008:08:28:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.121.132.53 - - [24/Feb/2008:08:28:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.226.58.44 - - [24/Feb/2008:08:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.226.58.44 - - [24/Feb/2008:08:28:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +82.226.58.44 - - [24/Feb/2008:08:28:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +82.226.58.44 - - [24/Feb/2008:08:28:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 +82.226.58.44 - - [24/Feb/2008:08:29:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.226.58.44 - - [24/Feb/2008:08:29:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +82.226.58.44 - - [24/Feb/2008:08:29:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +88.191.19.81 - - [24/Feb/2008:08:30:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.29.115.55 - - [24/Feb/2008:08:31:57 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.117 - - [24/Feb/2008:08:36:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [24/Feb/2008:08:36:42 -0600] "GET /swill/exec.html HTTP/1.1" 304 - +59.92.203.99 - - [24/Feb/2008:08:42:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +59.92.203.99 - - [24/Feb/2008:08:42:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.92.203.99 - - [24/Feb/2008:08:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:47:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.182.112.231 - - [24/Feb/2008:08:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +79.182.112.231 - - [24/Feb/2008:08:47:29 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +79.182.112.231 - - [24/Feb/2008:08:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +79.182.112.231 - - [24/Feb/2008:08:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +79.182.112.231 - - [24/Feb/2008:08:47:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +79.182.112.231 - - [24/Feb/2008:08:47:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +79.182.112.231 - - [24/Feb/2008:08:47:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +79.182.112.231 - - [24/Feb/2008:08:48:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +79.182.112.231 - - [24/Feb/2008:08:48:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +189.70.147.197 - - [24/Feb/2008:09:08:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.129.99.56 - - [24/Feb/2008:09:08:05 -0600] "GET /ply/ HTTP/1.1" 304 - +189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:08:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:08:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:08:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:09:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:12:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:09:17:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:17:55 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +189.70.147.197 - - [24/Feb/2008:09:18:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:18:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:18:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:18:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.184.6.249 - - [24/Feb/2008:09:18:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.184.6.249 - - [24/Feb/2008:09:18:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:19:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:20:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.184.6.249 - - [24/Feb/2008:09:21:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +150.210.155.167 - - [24/Feb/2008:09:22:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/ HTTP/1.0" 200 8018 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +76.68.215.63 - - [24/Feb/2008:09:28:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [24/Feb/2008:09:32:37 -0600] "GET /photos/u505/pages/IMG_1484.htm HTTP/1.1" 404 133 +84.122.84.241 - - [24/Feb/2008:09:32:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.122.84.241 - - [24/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.122.84.241 - - [24/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +118.83.147.252 - - [24/Feb/2008:09:35:15 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 +201.21.24.169 - - [24/Feb/2008:09:35:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:35:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:37:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.142 - - [24/Feb/2008:09:37:32 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 +189.13.155.147 - - [24/Feb/2008:09:38:31 -0600] "GET / HTTP/1.0" 200 4447 +189.13.155.147 - - [24/Feb/2008:09:38:32 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/ HTTP/1.1" 304 - +189.13.155.147 - - [24/Feb/2008:09:38:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +67.176.147.11 - - [24/Feb/2008:09:40:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +67.176.147.11 - - [24/Feb/2008:09:40:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +74.6.20.36 - - [24/Feb/2008:09:41:59 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.0" 200 399 +66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 +66.94.237.140 - - [24/Feb/2008:09:46:14 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +220.133.118.213 - - [24/Feb/2008:09:46:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.21.24.169 - - [24/Feb/2008:09:48:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.21.24.169 - - [24/Feb/2008:09:48:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:22 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [24/Feb/2008:09:54:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [24/Feb/2008:09:54:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +80.68.93.199 - - [24/Feb/2008:09:55:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 +80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +80.68.93.199 - - [24/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.68.93.199 - - [24/Feb/2008:09:56:26 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.23.14 - - [24/Feb/2008:09:57:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 +189.70.147.197 - - [24/Feb/2008:09:57:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:09:58:50 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.38.123 - - [24/Feb/2008:09:58:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:09:58:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:09:59:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.70.147.197 - - [24/Feb/2008:10:00:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:10:01:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:10:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.8.73 - - [24/Feb/2008:10:02:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.110.186.118 - - [24/Feb/2008:10:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.186.118 - - [24/Feb/2008:10:03:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +217.196.43.134 - - [24/Feb/2008:10:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.116.72.114 - - [24/Feb/2008:10:06:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:10:06:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.203.175.16 - - [24/Feb/2008:10:12:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.25.105 - - [24/Feb/2008:10:13:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +128.143.38.123 - - [24/Feb/2008:10:31:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.32 - - [24/Feb/2008:10:31:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.32 - - [24/Feb/2008:10:31:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.8.73 - - [24/Feb/2008:10:34:02 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +122.55.52.10 - - [24/Feb/2008:10:40:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +122.55.52.10 - - [24/Feb/2008:10:40:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +122.55.52.10 - - [24/Feb/2008:10:40:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +122.55.52.10 - - [24/Feb/2008:10:41:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +189.13.184.120 - - [24/Feb/2008:10:42:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.184.120 - - [24/Feb/2008:10:42:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +122.55.52.10 - - [24/Feb/2008:10:42:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.0" 200 3594 +189.13.184.120 - - [24/Feb/2008:10:44:41 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +88.191.19.81 - - [24/Feb/2008:10:46:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.8.73 - - [24/Feb/2008:10:56:58 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 +61.135.166.102 - - [24/Feb/2008:11:04:25 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [24/Feb/2008:11:23:15 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.1" 304 - +86.157.4.107 - - [24/Feb/2008:11:24:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.104.157 - - [24/Feb/2008:11:25:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +189.29.245.80 - - [24/Feb/2008:11:25:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.29.245.80 - - [24/Feb/2008:11:25:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.29.245.80 - - [24/Feb/2008:11:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 15602 +80.99.169.3 - - [24/Feb/2008:11:26:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.169.3 - - [24/Feb/2008:11:26:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.29.245.80 - - [24/Feb/2008:11:26:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:26:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.99.169.3 - - [24/Feb/2008:11:27:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:11:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.124.22.177 - - [24/Feb/2008:11:35:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:11:47:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:11:47:12 -0600] "GET /dynamic/ HTTP/1.0" 200 5105 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.199.195 - - [24/Feb/2008:11:54:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.199.195 - - [24/Feb/2008:11:54:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.206.180.194 - - [24/Feb/2008:11:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.206.180.194 - - [24/Feb/2008:11:59:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.71.34.82 - - [24/Feb/2008:12:02:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.71.34.82 - - [24/Feb/2008:12:02:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +83.71.34.82 - - [24/Feb/2008:12:02:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 +67.195.58.174 - - [24/Feb/2008:12:09:31 -0600] "GET /ply/ HTTP/1.0" 304 - +72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +72.240.122.140 - - [24/Feb/2008:12:18:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.240.122.140 - - [24/Feb/2008:12:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +72.240.122.140 - - [24/Feb/2008:12:19:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.117.28.14 - - [24/Feb/2008:12:24:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.117.28.14 - - [24/Feb/2008:12:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:12:28:47 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +24.1.247.118 - - [24/Feb/2008:12:29:32 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +24.1.247.118 - - [24/Feb/2008:12:29:37 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 731143 +98.206.164.173 - - [24/Feb/2008:12:33:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +98.206.164.173 - - [24/Feb/2008:12:33:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:12:37:58 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1878 +76.114.65.194 - - [24/Feb/2008:12:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +76.114.65.194 - - [24/Feb/2008:12:38:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +85.242.185.241 - - [24/Feb/2008:12:41:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.242.185.241 - - [24/Feb/2008:12:41:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.242.185.241 - - [24/Feb/2008:12:41:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.195 - - [24/Feb/2008:12:41:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:41:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.242.185.241 - - [24/Feb/2008:12:41:58 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +85.242.185.241 - - [24/Feb/2008:12:41:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.10.100.10 - - [24/Feb/2008:12:42:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.10.100.10 - - [24/Feb/2008:12:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [24/Feb/2008:12:44:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.195 - - [24/Feb/2008:12:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.137 - - [24/Feb/2008:12:50:05 -0600] "GET /photos/u505/pages/IMG_1500.htm HTTP/1.0" 404 133 +128.143.231.195 - - [24/Feb/2008:12:53:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:56:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:58:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:12:59:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.166.102 - - [24/Feb/2008:13:04:29 -0600] "GET / HTTP/1.1" 200 4447 +85.155.44.41 - - [24/Feb/2008:13:06:45 -0600] "GET /ply/ HTTP/1.1" 304 - +88.191.19.81 - - [24/Feb/2008:13:08:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +99.167.103.107 - - [24/Feb/2008:13:09:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.103.107 - - [24/Feb/2008:13:10:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +99.167.103.107 - - [24/Feb/2008:13:10:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.103.107 - - [24/Feb/2008:13:10:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.60.232.105 - - [24/Feb/2008:13:10:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.60.232.105 - - [24/Feb/2008:13:10:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.60.232.105 - - [24/Feb/2008:13:11:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.123 - - [24/Feb/2008:13:12:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.42.166.20 - - [24/Feb/2008:13:13:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +201.42.166.20 - - [24/Feb/2008:13:13:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:13:14:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.173.185.186 - - [24/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:13:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 126926 +128.143.231.195 - - [24/Feb/2008:13:20:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +67.195.58.177 - - [24/Feb/2008:13:20:46 -0600] "GET /python.html HTTP/1.0" 304 - +217.219.18.80 - - [24/Feb/2008:13:22:10 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +83.130.163.210 - - [24/Feb/2008:13:23:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +83.130.163.210 - - [24/Feb/2008:13:23:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.114.65.194 - - [24/Feb/2008:13:24:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +128.143.38.123 - - [24/Feb/2008:13:24:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.28 - - [24/Feb/2008:13:24:50 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1299 +76.114.65.194 - - [24/Feb/2008:13:25:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +76.114.65.194 - - [24/Feb/2008:13:25:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +76.114.65.194 - - [24/Feb/2008:13:26:19 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +76.114.65.194 - - [24/Feb/2008:13:26:35 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +76.114.65.194 - - [24/Feb/2008:13:27:21 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 +86.157.119.197 - - [24/Feb/2008:13:27:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.80.194.91 - - [24/Feb/2008:13:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:13:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.209.71.13 - - [24/Feb/2008:13:32:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +201.141.81.60 - - [24/Feb/2008:13:32:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.141.81.60 - - [24/Feb/2008:13:32:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.81.60 - - [24/Feb/2008:13:33:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +201.141.81.60 - - [24/Feb/2008:13:33:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.151 - - [24/Feb/2008:13:35:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +98.206.164.173 - - [24/Feb/2008:13:39:37 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +74.6.22.143 - - [24/Feb/2008:13:45:25 -0600] "GET /writing.html HTTP/1.0" 200 2871 +66.232.113.62 - - [24/Feb/2008:13:46:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +80.227.1.100 - - [24/Feb/2008:13:46:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +200.51.41.29 - - [24/Feb/2008:13:46:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.42.166.20 - - [24/Feb/2008:13:46:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +201.42.166.20 - - [24/Feb/2008:13:46:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +201.42.166.20 - - [24/Feb/2008:13:46:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +201.42.166.20 - - [24/Feb/2008:13:46:58 -0600] "GET /cgi-bin/wiki.pl?FormattingRules HTTP/1.1" 200 10503 +201.42.166.20 - - [24/Feb/2008:13:46:59 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +201.42.166.20 - - [24/Feb/2008:13:47:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +190.64.107.172 - - [24/Feb/2008:13:48:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.64.107.172 - - [24/Feb/2008:13:48:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +190.64.107.172 - - [24/Feb/2008:13:48:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.58.205.41 - - [24/Feb/2008:13:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.58.205.41 - - [24/Feb/2008:13:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.41 - - [24/Feb/2008:13:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +74.6.25.20 - - [24/Feb/2008:13:54:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [24/Feb/2008:13:54:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.143.231.195 - - [24/Feb/2008:13:54:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.168.250.62 - - [24/Feb/2008:13:57:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +70.168.250.62 - - [24/Feb/2008:13:57:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 991 +70.168.250.62 - - [24/Feb/2008:13:57:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1237 +70.168.250.62 - - [24/Feb/2008:13:57:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +69.209.71.13 - - [24/Feb/2008:13:59:17 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +69.209.71.13 - - [24/Feb/2008:13:59:25 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +70.168.250.62 - - [24/Feb/2008:13:59:30 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 +69.209.71.13 - - [24/Feb/2008:13:59:46 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +69.209.71.13 - - [24/Feb/2008:14:00:02 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.24.206.144 - - [24/Feb/2008:14:02:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.24.206.144 - - [24/Feb/2008:14:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.168.250.62 - - [24/Feb/2008:14:04:39 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1564 +70.168.250.62 - - [24/Feb/2008:14:04:42 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +74.6.19.115 - - [24/Feb/2008:14:04:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [24/Feb/2008:14:04:49 -0600] "GET /ply/ HTTP/1.0" 200 8018 +58.24.206.144 - - [24/Feb/2008:14:09:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.24.206.144 - - [24/Feb/2008:14:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +58.24.206.144 - - [24/Feb/2008:14:09:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +24.1.247.118 - - [24/Feb/2008:14:09:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.173.185.186 - - [24/Feb/2008:14:15:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +67.173.185.186 - - [24/Feb/2008:14:16:00 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.123 - - [24/Feb/2008:14:21:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.123 - - [24/Feb/2008:14:21:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.123 - - [24/Feb/2008:14:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.255.203.160 - - [24/Feb/2008:14:21:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.255.203.160 - - [24/Feb/2008:14:21:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.255.203.160 - - [24/Feb/2008:14:23:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.178 - - [24/Feb/2008:14:26:26 -0600] "GET /software.html HTTP/1.0" 304 - +67.195.58.164 - - [24/Feb/2008:14:27:22 -0600] "GET /dynamic/index.html HTTP/1.0" 304 - +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:30:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:30:27 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/ HTTP/1.1" 304 - +199.111.229.93 - - [24/Feb/2008:14:34:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +199.111.229.93 - - [24/Feb/2008:14:34:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:14:37:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.172 - - [24/Feb/2008:14:40:05 -0600] "GET /index.html HTTP/1.0" 200 4447 +65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.114 - - [24/Feb/2008:14:41:42 -0600] "GET /images/superboard.jpg HTTP/1.0" 200 71119 +204.111.252.233 - - [24/Feb/2008:14:42:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [24/Feb/2008:14:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.168 - - [24/Feb/2008:14:46:46 -0600] "GET /consulting.html HTTP/1.0" 304 - +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [24/Feb/2008:14:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:14:57:30 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +98.193.69.179 - - [24/Feb/2008:14:58:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +189.13.184.120 - - [24/Feb/2008:14:59:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.173.185.186 - - [24/Feb/2008:15:00:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.166.102 - - [24/Feb/2008:15:04:25 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [24/Feb/2008:15:04:57 -0600] "GET / HTTP/1.1" 200 4447 +217.237.150.206 - - [24/Feb/2008:15:06:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +217.237.150.208 - - [24/Feb/2008:15:06:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.237.150.208 - - [24/Feb/2008:15:06:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 963 +217.237.150.207 - - [24/Feb/2008:15:06:21 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +67.173.185.186 - - [24/Feb/2008:15:10:17 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 200 313896 +67.173.185.186 - - [24/Feb/2008:15:10:18 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 206 665908 +210.212.55.3 - - [24/Feb/2008:15:18:15 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.25.124 - - [24/Feb/2008:15:18:35 -0600] "GET /photos/u505/pages/IMG_1524.htm HTTP/1.0" 404 133 +210.212.55.3 - - [24/Feb/2008:15:18:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply HTTP/1.1" 301 242 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/ HTTP/1.1" 206 1042 +128.12.159.47 - - [24/Feb/2008:15:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.212.55.3 - - [24/Feb/2008:15:25:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.54.165.36 - - [24/Feb/2008:15:37:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.165.36 - - [24/Feb/2008:15:37:17 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - +69.46.29.140 - - [24/Feb/2008:15:40:50 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 +91.121.92.62 - - [24/Feb/2008:15:40:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +206.51.237.114 - - [24/Feb/2008:15:41:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +88.255.192.42 - - [24/Feb/2008:15:41:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +83.229.21.4 - - [24/Feb/2008:15:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +85.214.114.155 - - [24/Feb/2008:15:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +210.22.158.132 - - [24/Feb/2008:15:41:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +66.232.113.194 - - [24/Feb/2008:15:43:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +60.234.20.98 - - [24/Feb/2008:15:43:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.216.186.35 - - [24/Feb/2008:15:43:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.143 - - [24/Feb/2008:15:50:37 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply HTTP/1.1" 301 242 +86.138.167.172 - - [24/Feb/2008:15:54:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.138.167.172 - - [24/Feb/2008:15:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [24/Feb/2008:15:56:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [24/Feb/2008:15:56:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [24/Feb/2008:16:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.199.136 - - [24/Feb/2008:16:05:51 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.199.136 - - [24/Feb/2008:16:05:53 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1149 +89.229.25.165 - - [24/Feb/2008:16:06:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.229.25.165 - - [24/Feb/2008:16:06:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.229.25.165 - - [24/Feb/2008:16:07:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +75.32.37.138 - - [24/Feb/2008:16:12:29 -0600] "GET / HTTP/1.1" 200 4447 +75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +75.32.37.138 - - [24/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [24/Feb/2008:16:13:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +75.32.37.138 - - [24/Feb/2008:16:13:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +204.111.252.233 - - [24/Feb/2008:16:14:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [24/Feb/2008:16:15:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +85.185.11.131 - - [24/Feb/2008:16:15:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +64.124.150.55 - - [24/Feb/2008:16:15:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [24/Feb/2008:16:20:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET / HTTP/1.1" 200 4447 +128.135.11.245 - - [24/Feb/2008:16:21:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +172.159.46.8 - - [24/Feb/2008:16:21:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +172.159.46.8 - - [24/Feb/2008:16:21:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +128.135.11.245 - - [24/Feb/2008:16:21:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +172.159.46.8 - - [24/Feb/2008:16:21:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:21:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.39 - - [24/Feb/2008:16:23:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [24/Feb/2008:16:25:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [24/Feb/2008:16:25:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.42.43 - - [24/Feb/2008:16:30:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.176.224.242 - - [24/Feb/2008:16:30:34 -0600] "GET /robots.txt HTTP/1.0" 200 71 +89.229.25.165 - - [24/Feb/2008:16:33:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.89.137.229 - - [24/Feb/2008:16:33:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.89.137.229 - - [24/Feb/2008:16:33:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.6.151.80 - - [24/Feb/2008:16:39:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.11.245 - - [24/Feb/2008:16:40:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [24/Feb/2008:16:40:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.4.230.43 - - [24/Feb/2008:16:40:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +64.4.230.43 - - [24/Feb/2008:16:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.4.230.43 - - [24/Feb/2008:16:40:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +74.6.19.101 - - [24/Feb/2008:16:46:54 -0600] "GET /swill/Doc/ HTTP/1.0" 200 39052 +67.173.185.186 - - [24/Feb/2008:16:48:30 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.62.148.145 - - [24/Feb/2008:16:56:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.62.148.145 - - [24/Feb/2008:16:56:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:56:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:56:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [24/Feb/2008:16:57:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.181.38.169 - - [24/Feb/2008:17:04:19 -0600] "GET / HTTP/1.1" 200 4447 +61.135.166.102 - - [24/Feb/2008:17:04:25 -0600] "GET / HTTP/1.1" 200 4447 +217.196.43.134 - - [24/Feb/2008:17:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.17 - - [24/Feb/2008:17:07:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.17 - - [24/Feb/2008:17:07:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.10 - - [24/Feb/2008:17:09:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.205.10 - - [24/Feb/2008:17:10:10 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.202.87.9 - - [24/Feb/2008:17:12:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +199.111.205.10 - - [24/Feb/2008:17:14:56 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.62.148.145 - - [24/Feb/2008:17:18:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [24/Feb/2008:17:19:36 -0600] "GET /ply/README HTTP/1.1" 200 8605 +71.62.148.145 - - [24/Feb/2008:17:21:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +80.252.151.118 - - [24/Feb/2008:17:21:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +199.111.205.10 - - [24/Feb/2008:17:22:18 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.248.57.218 - - [24/Feb/2008:17:24:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.248.57.218 - - [24/Feb/2008:17:24:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.144.107.121 - - [24/Feb/2008:17:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:26:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +69.209.71.13 - - [24/Feb/2008:17:26:43 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +71.62.148.145 - - [24/Feb/2008:17:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [24/Feb/2008:17:33:12 -0600] "GET / HTTP/1.1" 200 4447 +71.62.148.145 - - [24/Feb/2008:17:37:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.144.107.121 - - [24/Feb/2008:17:37:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.195.58.182 - - [24/Feb/2008:17:38:07 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +67.195.58.186 - - [24/Feb/2008:17:38:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +67.195.58.170 - - [24/Feb/2008:17:38:32 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +67.195.58.181 - - [24/Feb/2008:17:38:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +67.173.185.186 - - [24/Feb/2008:17:38:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.151 - - [24/Feb/2008:17:39:02 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +189.144.107.121 - - [24/Feb/2008:17:40:22 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +189.144.107.121 - - [24/Feb/2008:17:40:23 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +76.198.207.51 - - [24/Feb/2008:17:44:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.198.207.51 - - [24/Feb/2008:17:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:17:47:25 -0600] "GET /python.html HTTP/1.1" 200 18870 +151.33.39.177 - - [24/Feb/2008:17:47:26 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:17:47:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.91.200.98 - - [24/Feb/2008:17:48:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.195.58.164 - - [24/Feb/2008:17:54:04 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +76.10.149.199 - - [24/Feb/2008:17:54:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.10.149.199 - - [24/Feb/2008:17:54:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.10.149.199 - - [24/Feb/2008:17:54:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +72.244.56.83 - - [24/Feb/2008:17:54:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +72.244.56.83 - - [24/Feb/2008:17:54:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.244.56.83 - - [24/Feb/2008:17:55:22 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +65.247.230.214 - - [24/Feb/2008:17:56:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.10.149.199 - - [24/Feb/2008:17:56:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +76.10.149.199 - - [24/Feb/2008:17:56:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +76.10.149.199 - - [24/Feb/2008:17:56:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 +65.55.208.118 - - [24/Feb/2008:17:57:44 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [24/Feb/2008:17:57:45 -0600] "GET /photos/u505/pages/IMG_1530.htm HTTP/1.1" 404 133 +74.6.19.115 - - [24/Feb/2008:17:59:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.145 - - [24/Feb/2008:17:59:40 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +88.191.19.81 - - [24/Feb/2008:18:01:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.22.143 - - [24/Feb/2008:18:02:06 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +71.230.189.170 - - [24/Feb/2008:18:06:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +151.33.39.177 - - [24/Feb/2008:18:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.33.39.177 - - [24/Feb/2008:18:10:37 -0600] "GET /software.html HTTP/1.1" 200 3163 +84.110.216.199 - - [24/Feb/2008:18:17:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.216.199 - - [24/Feb/2008:18:17:33 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 +67.195.58.168 - - [24/Feb/2008:18:17:51 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +64.81.241.54 - - [24/Feb/2008:18:26:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.241.54 - - [24/Feb/2008:18:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [24/Feb/2008:18:32:02 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.146.214.212 - - [24/Feb/2008:18:32:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +12.206.63.189 - - [24/Feb/2008:18:37:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.206.63.189 - - [24/Feb/2008:18:37:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.206.63.189 - - [24/Feb/2008:18:38:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.70.18 - - [24/Feb/2008:18:42:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +82.35.89.141 - - [24/Feb/2008:18:43:23 -0600] "GET /ply/ HTTP/1.1" 304 - +82.35.89.141 - - [24/Feb/2008:18:43:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +82.226.58.44 - - [24/Feb/2008:18:45:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.226.58.44 - - [24/Feb/2008:18:45:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.226.58.44 - - [24/Feb/2008:18:45:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +82.226.58.44 - - [24/Feb/2008:18:46:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +128.143.70.18 - - [24/Feb/2008:18:48:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.110.209.197 - - [24/Feb/2008:18:56:42 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.209.197 - - [24/Feb/2008:18:56:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 +58.215.57.238 - - [24/Feb/2008:18:59:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.215.57.238 - - [24/Feb/2008:18:59:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.215.57.238 - - [24/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET /robots.txt HTTP/1.0" 200 71 +216.145.11.94 - - [24/Feb/2008:19:02:32 -0600] "GET / HTTP/1.1" 206 4447 +218.94.136.173 - - [24/Feb/2008:19:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.94.136.173 - - [24/Feb/2008:19:05:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.94.136.173 - - [24/Feb/2008:19:05:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.125.38.188 - - [24/Feb/2008:19:12:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +74.6.22.143 - - [24/Feb/2008:19:21:03 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 +72.85.134.143 - - [24/Feb/2008:19:23:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.85.134.143 - - [24/Feb/2008:19:23:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +75.22.21.146 - - [24/Feb/2008:19:25:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.85.134.143 - - [24/Feb/2008:19:25:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.207.163.47 - - [24/Feb/2008:19:30:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.207.163.47 - - [24/Feb/2008:19:30:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.207.163.47 - - [24/Feb/2008:19:30:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:31:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.207.163.47 - - [24/Feb/2008:19:31:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +210.245.52.8 - - [24/Feb/2008:19:38:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.245.52.8 - - [24/Feb/2008:19:38:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.141.19.88 - - [24/Feb/2008:19:42:42 -0600] "GET /ply/ HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +189.141.19.88 - - [24/Feb/2008:19:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.141.19.88 - - [24/Feb/2008:19:42:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.57.91.136 - - [24/Feb/2008:19:46:56 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.84.190.11 - - [24/Feb/2008:19:48:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.52 - - [24/Feb/2008:19:49:28 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [24/Feb/2008:19:49:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +24.84.190.11 - - [24/Feb/2008:19:50:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +74.6.28.151 - - [24/Feb/2008:19:53:29 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1335 +201.86.78.151 - - [24/Feb/2008:19:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.86.78.151 - - [24/Feb/2008:19:54:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:54:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:54:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.86.78.151 - - [24/Feb/2008:19:55:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.190.17 - - [24/Feb/2008:19:58:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.28.162 - - [24/Feb/2008:19:59:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 +99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +99.140.184.199 - - [24/Feb/2008:20:00:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.184.199 - - [24/Feb/2008:20:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [24/Feb/2008:20:00:49 -0600] "GET /dynamic/ HTTP/1.1" 304 - +75.22.21.146 - - [24/Feb/2008:20:01:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [24/Feb/2008:20:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:03:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:04:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:20:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +201.141.93.154 - - [24/Feb/2008:20:05:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.141.93.154 - - [24/Feb/2008:20:05:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.141.93.154 - - [24/Feb/2008:20:05:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:06:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:06:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.143.35.13 - - [24/Feb/2008:20:09:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.143.35.13 - - [24/Feb/2008:20:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +165.95.24.194 - - [24/Feb/2008:20:10:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.141.93.154 - - [24/Feb/2008:20:13:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.209.71.13 - - [24/Feb/2008:20:21:31 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +189.13.184.120 - - [24/Feb/2008:20:33:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.184.120 - - [24/Feb/2008:20:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.13.184.120 - - [24/Feb/2008:20:44:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.58.174 - - [24/Feb/2008:20:44:24 -0600] "GET /ply/ HTTP/1.0" 304 - +165.82.137.78 - - [24/Feb/2008:20:45:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +67.195.58.160 - - [24/Feb/2008:20:45:23 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +67.195.58.174 - - [24/Feb/2008:20:45:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +67.195.58.188 - - [24/Feb/2008:20:46:00 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +67.195.58.178 - - [24/Feb/2008:20:46:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +67.186.98.20 - - [24/Feb/2008:20:48:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +67.186.98.20 - - [24/Feb/2008:20:48:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [24/Feb/2008:20:48:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.107.0.112 - - [24/Feb/2008:20:54:44 -0600] "GET /robots.txt HTTP/1.1" 304 - +131.107.0.112 - - [24/Feb/2008:20:55:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +220.237.12.253 - - [24/Feb/2008:20:56:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:56:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:58:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.237.12.253 - - [24/Feb/2008:20:59:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.176.147.11 - - [24/Feb/2008:20:59:21 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +189.13.184.120 - - [24/Feb/2008:21:01:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +165.82.137.78 - - [24/Feb/2008:21:02:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +165.82.137.78 - - [24/Feb/2008:21:02:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +165.82.137.78 - - [24/Feb/2008:21:03:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +61.135.166.102 - - [24/Feb/2008:21:04:22 -0600] "GET / HTTP/1.1" 200 4447 +220.237.12.253 - - [24/Feb/2008:21:04:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:21:04:29 -0600] "GET /swill/writing.html HTTP/1.0" 404 133 +220.237.12.253 - - [24/Feb/2008:21:05:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +68.72.123.219 - - [24/Feb/2008:21:06:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +125.16.133.35 - - [24/Feb/2008:21:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [24/Feb/2008:21:08:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.137.78 - - [24/Feb/2008:21:10:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +165.82.137.78 - - [24/Feb/2008:21:10:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +165.82.137.78 - - [24/Feb/2008:21:10:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +83.249.251.158 - - [24/Feb/2008:21:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.249.251.158 - - [24/Feb/2008:21:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [24/Feb/2008:21:11:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.72.123.219 - - [24/Feb/2008:21:12:25 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +68.72.123.219 - - [24/Feb/2008:21:12:32 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +74.6.20.207 - - [24/Feb/2008:21:18:07 -0600] "GET /writing.html HTTP/1.0" 200 2871 +220.237.12.253 - - [24/Feb/2008:21:28:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:30:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:21:31:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +202.181.80.140 - - [24/Feb/2008:21:34:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +202.181.80.140 - - [24/Feb/2008:21:34:16 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.181.80.140 - - [24/Feb/2008:21:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +202.181.80.140 - - [24/Feb/2008:21:35:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +139.175.68.252 - - [24/Feb/2008:21:40:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +88.191.19.81 - - [24/Feb/2008:21:44:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.219.203.174 - - [24/Feb/2008:21:45:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.219.203.174 - - [24/Feb/2008:21:45:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +86.219.203.174 - - [24/Feb/2008:21:48:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +86.219.203.174 - - [24/Feb/2008:21:48:03 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +67.175.229.192 - - [24/Feb/2008:21:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:21:50:37 -0600] "GET /cv.html HTTP/1.0" 200 31798 +67.176.147.11 - - [24/Feb/2008:22:03:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +189.6.242.136 - - [24/Feb/2008:22:12:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.6.242.136 - - [24/Feb/2008:22:12:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.6.242.136 - - [24/Feb/2008:22:12:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.6.242.136 - - [24/Feb/2008:22:15:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [24/Feb/2008:22:20:29 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [24/Feb/2008:22:20:56 -0600] "GET / HTTP/1.1" 200 4447 +207.229.184.99 - - [24/Feb/2008:22:22:52 -0600] "GET /dynamic/ HTTP/1.1" 304 - +207.229.184.99 - - [24/Feb/2008:22:22:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +74.6.28.156 - - [24/Feb/2008:22:26:48 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +74.6.24.162 - - [24/Feb/2008:22:29:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 +74.6.26.198 - - [24/Feb/2008:22:30:06 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +139.175.68.252 - - [24/Feb/2008:22:33:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:22:33:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:22:33:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.200 - - [24/Feb/2008:22:48:41 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +220.181.38.169 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 +61.135.166.102 - - [24/Feb/2008:23:04:32 -0600] "GET / HTTP/1.1" 200 4447 +68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.83.161.100 - - [24/Feb/2008:23:06:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.83.161.100 - - [24/Feb/2008:23:06:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.34.230.143 - - [24/Feb/2008:23:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.34.230.143 - - [24/Feb/2008:23:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +58.211.255.253 - - [24/Feb/2008:23:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.211.255.253 - - [24/Feb/2008:23:11:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.116.72.114 - - [24/Feb/2008:23:13:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [24/Feb/2008:23:13:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.120 - - [24/Feb/2008:23:14:09 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.0" 200 375 +207.229.184.99 - - [24/Feb/2008:23:22:11 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +207.229.184.99 - - [24/Feb/2008:23:22:22 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +74.6.22.143 - - [24/Feb/2008:23:35:43 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1210 +67.186.98.20 - - [24/Feb/2008:23:35:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [24/Feb/2008:23:39:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:39:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [24/Feb/2008:23:40:11 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +139.175.68.252 - - [24/Feb/2008:23:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [24/Feb/2008:23:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.247.172.9 - - [24/Feb/2008:23:49:24 -0600] "GET / HTTP/1.1" 200 4447 +41.196.193.85 - - [24/Feb/2008:23:49:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.196.193.85 - - [24/Feb/2008:23:49:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +41.196.193.85 - - [24/Feb/2008:23:49:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.84.154.13 - - [24/Feb/2008:23:50:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.106 - - [24/Feb/2008:23:55:14 -0600] "GET /photos/u505/ HTTP/1.0" 404 133 +217.196.43.134 - - [25/Feb/2008:00:05:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.242.107.51 - - [25/Feb/2008:00:11:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.145.165.82 - - [25/Feb/2008:00:15:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.206.161.230 - - [25/Feb/2008:00:16:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +138.206.161.230 - - [25/Feb/2008:00:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +138.206.161.230 - - [25/Feb/2008:00:16:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +148.243.90.225 - - [25/Feb/2008:00:17:55 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.22.143 - - [25/Feb/2008:00:18:25 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +57.73.25.166 - - [25/Feb/2008:00:22:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:00:24:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.143 - - [25/Feb/2008:00:24:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +74.6.25.20 - - [25/Feb/2008:00:26:13 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.169 - - [25/Feb/2008:00:26:14 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +211.127.232.14 - - [25/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.127.232.14 - - [25/Feb/2008:00:26:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +211.127.232.14 - - [25/Feb/2008:00:26:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +211.127.232.14 - - [25/Feb/2008:00:28:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +211.127.232.14 - - [25/Feb/2008:00:28:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2813 +211.127.232.14 - - [25/Feb/2008:00:28:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +211.127.232.14 - - [25/Feb/2008:00:29:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Perl5Typemaps HTTP/1.1" 200 3613 +211.127.232.14 - - [25/Feb/2008:00:29:38 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.192.28.26 - - [25/Feb/2008:00:34:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.192.28.26 - - [25/Feb/2008:00:35:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.192.28.26 - - [25/Feb/2008:00:37:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +71.192.28.26 - - [25/Feb/2008:00:37:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.195.58.175 - - [25/Feb/2008:00:46:01 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +74.6.22.143 - - [25/Feb/2008:00:48:20 -0600] "GET /index.html HTTP/1.0" 200 4447 +88.191.19.81 - - [25/Feb/2008:00:48:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [25/Feb/2008:00:49:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.126.89.51 - - [25/Feb/2008:00:50:17 -0600] "GET / HTTP/1.1" 200 4447 +86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply HTTP/1.1" 301 242 +86.15.171.18 - - [25/Feb/2008:00:58:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +125.35.5.39 - - [25/Feb/2008:01:02:32 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +125.35.5.39 - - [25/Feb/2008:01:02:33 -0600] "GET /ply/ HTTP/1.0" 200 8018 +125.35.5.39 - - [25/Feb/2008:01:02:34 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +146.230.128.29 - - [25/Feb/2008:01:03:58 -0600] "GET /cv.html HTTP/1.0" 200 31798 +146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +146.230.128.29 - - [25/Feb/2008:01:03:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.135.166.102 - - [25/Feb/2008:01:04:27 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:01:04:54 -0600] "GET / HTTP/1.1" 200 4447 +146.230.128.29 - - [25/Feb/2008:01:05:20 -0600] "GET / HTTP/1.0" 200 4447 +146.230.128.29 - - [25/Feb/2008:01:05:21 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 +139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +139.175.68.252 - - [25/Feb/2008:01:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +139.175.68.252 - - [25/Feb/2008:01:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +146.230.128.29 - - [25/Feb/2008:01:06:15 -0600] "GET /python.html HTTP/1.0" 200 18870 +146.230.128.29 - - [25/Feb/2008:01:06:16 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +146.230.128.29 - - [25/Feb/2008:01:06:17 -0600] "GET /software.html HTTP/1.0" 200 3163 +146.230.128.29 - - [25/Feb/2008:01:06:33 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply HTTP/1.1" 301 242 +148.245.19.94 - - [25/Feb/2008:01:08:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.245.19.94 - - [25/Feb/2008:01:08:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.160 - - [25/Feb/2008:01:08:56 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - +71.192.28.26 - - [25/Feb/2008:01:10:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.175.68.252 - - [25/Feb/2008:01:18:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:01:18:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.172.44.82 - - [25/Feb/2008:01:22:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.80.193.48 - - [25/Feb/2008:01:22:20 -0600] "GET / HTTP/1.1" 200 4447 +198.54.202.210 - - [25/Feb/2008:01:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.54.202.194 - - [25/Feb/2008:01:25:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 +70.90.215.85 - - [25/Feb/2008:01:28:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +70.90.215.85 - - [25/Feb/2008:01:28:54 -0600] "GET /ply HTTP/1.0" 301 230 +70.90.215.85 - - [25/Feb/2008:01:28:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.78.221.48 - - [25/Feb/2008:01:29:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.78.221.48 - - [25/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +138.246.7.155 - - [25/Feb/2008:01:38:12 -0600] "GET /ply/ HTTP/1.1" 304 - +138.246.7.155 - - [25/Feb/2008:01:38:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.185.239.242 - - [25/Feb/2008:01:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +58.185.239.242 - - [25/Feb/2008:01:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.185.239.242 - - [25/Feb/2008:01:39:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +69.137.228.16 - - [25/Feb/2008:01:46:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.137.228.16 - - [25/Feb/2008:01:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.185.239.242 - - [25/Feb/2008:01:48:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +58.185.239.242 - - [25/Feb/2008:01:48:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +58.185.239.242 - - [25/Feb/2008:01:48:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +84.177.46.231 - - [25/Feb/2008:01:50:08 -0600] "GET /python.html HTTP/1.1" 200 18870 +84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +84.177.46.231 - - [25/Feb/2008:01:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.177.46.231 - - [25/Feb/2008:01:50:10 -0600] "GET /training.html HTTP/1.1" 200 6154 +206.51.237.114 - - [25/Feb/2008:01:51:23 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +88.255.192.42 - - [25/Feb/2008:01:51:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.247.11.155 - - [25/Feb/2008:01:51:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.65.127.161 - - [25/Feb/2008:01:51:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +71.38.14.119 - - [25/Feb/2008:01:54:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.38.14.119 - - [25/Feb/2008:01:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +69.137.228.16 - - [25/Feb/2008:01:57:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +138.246.7.155 - - [25/Feb/2008:02:01:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.194.30.240 - - [25/Feb/2008:02:04:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +87.194.30.240 - - [25/Feb/2008:02:04:30 -0600] "GET /ply HTTP/1.1" 301 242 +87.194.30.240 - - [25/Feb/2008:02:04:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.108.70.84 - - [25/Feb/2008:02:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.108.70.84 - - [25/Feb/2008:02:05:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +217.237.70.128 - - [25/Feb/2008:02:08:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.237.70.128 - - [25/Feb/2008:02:08:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.6.228.64 - - [25/Feb/2008:02:18:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.6.228.64 - - [25/Feb/2008:02:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.6.228.64 - - [25/Feb/2008:02:18:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +75.6.228.64 - - [25/Feb/2008:02:18:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/PhP HTTP/1.1" 200 3606 +75.6.228.64 - - [25/Feb/2008:02:18:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +66.232.113.194 - - [25/Feb/2008:02:22:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +200.133.15.2 - - [25/Feb/2008:02:22:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +219.93.178.162 - - [25/Feb/2008:02:22:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +217.255.38.100 - - [25/Feb/2008:02:28:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:28:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.196.6.232 - - [25/Feb/2008:02:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [25/Feb/2008:02:33:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.137.228.16 - - [25/Feb/2008:02:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +69.137.228.16 - - [25/Feb/2008:02:34:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:36:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.137.228.16 - - [25/Feb/2008:02:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.79.100.39 - - [25/Feb/2008:02:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.79.100.39 - - [25/Feb/2008:02:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1554 +130.79.100.39 - - [25/Feb/2008:02:41:24 -0600] "GET /cgi-bin/wiki.pl?UninstantiatedTemplates HTTP/1.1" 200 2091 +130.79.100.39 - - [25/Feb/2008:02:41:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.79.100.39 - - [25/Feb/2008:02:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +74.6.22.143 - - [25/Feb/2008:02:45:47 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.55.208.122 - - [25/Feb/2008:02:49:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [25/Feb/2008:02:49:52 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE076.HTM HTTP/1.1" 304 - +74.6.23.75 - - [25/Feb/2008:02:52:21 -0600] "GET /photos/u505/pages/IMG_1502.htm HTTP/1.0" 404 133 +217.255.38.100 - - [25/Feb/2008:02:54:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.255.38.100 - - [25/Feb/2008:02:54:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +81.255.106.203 - - [25/Feb/2008:02:56:49 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.22.143 - - [25/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +61.135.166.102 - - [25/Feb/2008:03:04:19 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:03:05:28 -0600] "GET / HTTP/1.1" 200 4447 +194.105.57.11 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.105.57.12 - - [25/Feb/2008:03:06:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.105.57.12 - - [25/Feb/2008:03:06:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.15 - - [25/Feb/2008:03:11:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.15 - - [25/Feb/2008:03:11:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +74.6.7.107 - - [25/Feb/2008:03:13:02 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.0" 200 288790 +151.96.0.8 - - [25/Feb/2008:03:13:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +151.96.0.8 - - [25/Feb/2008:03:13:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.185.76.213 - - [25/Feb/2008:03:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.185.76.213 - - [25/Feb/2008:03:16:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/ HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:22:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +193.190.210.85 - - [25/Feb/2008:03:23:06 -0600] "GET /ply/ HTTP/1.1" 304 - +203.200.35.12 - - [25/Feb/2008:03:28:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.200.35.12 - - [25/Feb/2008:03:28:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.200.35.12 - - [25/Feb/2008:03:28:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.200.35.12 - - [25/Feb/2008:03:28:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/ HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:29:43 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +203.200.218.2 - - [25/Feb/2008:03:29:55 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +203.200.218.2 - - [25/Feb/2008:03:30:17 -0600] "GET /python.html HTTP/1.0" 200 18870 +203.200.218.2 - - [25/Feb/2008:03:30:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +203.200.218.2 - - [25/Feb/2008:03:34:35 -0600] "GET /ply/ HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:34:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 +66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.200.218.2 - - [25/Feb/2008:03:34:36 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:34:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.54.42 - - [25/Feb/2008:03:34:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET / HTTP/1.1" 200 4447 +66.201.54.42 - - [25/Feb/2008:03:34:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +203.200.218.2 - - [25/Feb/2008:03:34:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /cv.html HTTP/1.1" 200 31798 +72.32.58.119 - - [25/Feb/2008:03:34:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [25/Feb/2008:03:35:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.1" 200 1232 +67.195.58.174 - - [25/Feb/2008:03:35:56 -0600] "GET /ply/ HTTP/1.0" 304 - +66.249.65.37 - - [25/Feb/2008:03:36:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 200 1336 +66.249.65.37 - - [25/Feb/2008:03:39:15 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE063.HTM HTTP/1.1" 200 984 +124.30.116.190 - - [25/Feb/2008:03:42:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.30.116.190 - - [25/Feb/2008:03:42:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [25/Feb/2008:03:44:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +124.30.116.190 - - [25/Feb/2008:03:44:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.142.212.161 - - [25/Feb/2008:03:51:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 +68.142.212.161 - - [25/Feb/2008:03:51:39 -0600] "GET /images/BadDave1.jpg HTTP/1.0" 304 - +139.175.68.252 - - [25/Feb/2008:03:53:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +139.175.68.252 - - [25/Feb/2008:03:53:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.142.212.161 - - [25/Feb/2008:03:55:08 -0600] "GET /images/davechina.jpg HTTP/1.0" 304 - +66.201.54.42 - - [25/Feb/2008:03:59:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.89.228.178 - - [25/Feb/2008:04:33:30 -0600] "GET / HTTP/1.0" 200 4447 +130.225.195.70 - - [25/Feb/2008:04:47:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.225.195.70 - - [25/Feb/2008:04:47:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.225.195.70 - - [25/Feb/2008:04:47:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.126.185 - - [25/Feb/2008:04:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.126.185 - - [25/Feb/2008:04:48:18 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +61.135.166.102 - - [25/Feb/2008:05:04:21 -0600] "GET / HTTP/1.1" 200 4447 +84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.122.157 - - [25/Feb/2008:05:08:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +130.225.195.70 - - [25/Feb/2008:05:12:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.154.251.108 - - [25/Feb/2008:05:13:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.154.251.108 - - [25/Feb/2008:05:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.154.251.108 - - [25/Feb/2008:05:13:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.154.251.108 - - [25/Feb/2008:05:14:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.154.251.108 - - [25/Feb/2008:05:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.154.251.108 - - [25/Feb/2008:05:14:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +74.6.20.35 - - [25/Feb/2008:05:24:35 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1474 +74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.150 - - [25/Feb/2008:05:26:54 -0600] "GET /swill/swill-0.1.tar.gz HTTP/1.0" 200 119170 +58.107.212.3 - - [25/Feb/2008:05:34:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.226.57.203 - - [25/Feb/2008:05:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:05:38:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:05:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +74.6.25.148 - - [25/Feb/2008:05:48:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.107.147.45 - - [25/Feb/2008:05:48:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.107.147.45 - - [25/Feb/2008:05:49:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.107.147.45 - - [25/Feb/2008:05:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +82.107.147.45 - - [25/Feb/2008:05:49:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +82.107.147.45 - - [25/Feb/2008:05:49:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +82.107.147.45 - - [25/Feb/2008:05:49:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +88.191.19.81 - - [25/Feb/2008:05:52:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:53:28 -0600] "GET / HTTP/1.1" 200 4447 +58.24.206.144 - - [25/Feb/2008:05:53:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +58.24.206.144 - - [25/Feb/2008:05:54:13 -0600] "GET /python.html HTTP/1.1" 200 18870 +58.24.206.144 - - [25/Feb/2008:05:54:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +58.24.206.144 - - [25/Feb/2008:05:54:34 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:54:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +131.159.46.32 - - [25/Feb/2008:05:54:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.24.206.144 - - [25/Feb/2008:05:55:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +89.102.3.36 - - [25/Feb/2008:05:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:05:57:56 -0600] "GET /dynamic/sd.html HTTP/1.1" 304 - +58.24.206.144 - - [25/Feb/2008:05:58:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.172.19.20 - - [25/Feb/2008:05:59:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.172.19.20 - - [25/Feb/2008:05:59:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.166.41.113 - - [25/Feb/2008:06:01:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +88.166.41.113 - - [25/Feb/2008:06:02:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:15:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.110.220.12 - - [25/Feb/2008:06:15:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.110.220.12 - - [25/Feb/2008:06:15:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:24:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.110.220.12 - - [25/Feb/2008:06:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +92.112.194.106 - - [25/Feb/2008:06:35:05 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [25/Feb/2008:06:38:49 -0600] "GET / HTTP/1.1" 200 4447 +125.16.133.35 - - [25/Feb/2008:06:44:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.246.7.155 - - [25/Feb/2008:06:45:59 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +200.21.98.7 - - [25/Feb/2008:06:50:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.21.98.7 - - [25/Feb/2008:06:50:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.240.69.90 - - [25/Feb/2008:06:52:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.240.69.90 - - [25/Feb/2008:06:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.240.69.90 - - [25/Feb/2008:06:52:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.57.248.115 - - [25/Feb/2008:06:55:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.57.248.115 - - [25/Feb/2008:06:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.21.126.135 - - [25/Feb/2008:06:55:07 -0600] "GET /ply/ HTTP/1.0" 200 8018 +192.54.144.229 - - [25/Feb/2008:06:55:20 -0600] "GET /ply HTTP/1.1" 301 242 +192.54.144.229 - - [25/Feb/2008:06:55:21 -0600] "GET /ply/ HTTP/1.1" 304 - +80.57.248.115 - - [25/Feb/2008:06:55:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +196.21.126.135 - - [25/Feb/2008:06:55:55 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +124.215.248.26 - - [25/Feb/2008:06:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:57:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:06:58:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:06:58:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +124.215.248.26 - - [25/Feb/2008:07:00:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.195.66.68 - - [25/Feb/2008:07:01:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.181.38.169 - - [25/Feb/2008:07:04:30 -0600] "GET / HTTP/1.1" 200 4447 +84.89.249.77 - - [25/Feb/2008:07:04:44 -0600] "GET /ply/ HTTP/1.1" 304 - +84.89.249.77 - - [25/Feb/2008:07:04:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.196.43.134 - - [25/Feb/2008:07:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.215.248.26 - - [25/Feb/2008:07:05:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.215.248.26 - - [25/Feb/2008:07:07:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.94.82.18 - - [25/Feb/2008:07:10:52 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +140.94.82.18 - - [25/Feb/2008:07:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.255.103.130 - - [25/Feb/2008:07:11:43 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.255.103.130 - - [25/Feb/2008:07:11:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 +200.255.103.130 - - [25/Feb/2008:07:12:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 408 +200.255.103.130 - - [25/Feb/2008:07:12:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 410 +200.255.103.130 - - [25/Feb/2008:07:12:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 912 +200.255.103.130 - - [25/Feb/2008:07:12:24 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 2460 +140.94.82.18 - - [25/Feb/2008:07:13:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 +130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.235.34.165 - - [25/Feb/2008:07:14:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:14:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.235.34.165 - - [25/Feb/2008:07:15:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.62.75.201 - - [25/Feb/2008:07:16:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.255.103.130 - - [25/Feb/2008:07:18:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET / HTTP/1.1" 200 4447 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +71.57.91.136 - - [25/Feb/2008:07:24:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:07:24:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +71.57.91.136 - - [25/Feb/2008:07:24:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.19.207 - - [25/Feb/2008:07:24:42 -0600] "GET /photos/wind/pages/IMG_1270.htm HTTP/1.0" 404 133 +82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.135.63.177 - - [25/Feb/2008:07:29:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.8.219.19 - - [25/Feb/2008:07:32:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:32:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.8.219.19 - - [25/Feb/2008:07:32:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.195.186.41 - - [25/Feb/2008:07:38:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +192.93.158.26 - - [25/Feb/2008:07:39:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +192.93.158.26 - - [25/Feb/2008:07:39:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.237.120.134 - - [25/Feb/2008:07:42:04 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.221.197.20 - - [25/Feb/2008:07:43:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +124.30.116.190 - - [25/Feb/2008:07:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +74.6.22.150 - - [25/Feb/2008:07:54:27 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5105 +74.6.26.75 - - [25/Feb/2008:07:55:55 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 +84.237.120.134 - - [25/Feb/2008:07:56:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.195.186.41 - - [25/Feb/2008:07:56:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.195.186.41 - - [25/Feb/2008:07:56:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:08:01:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [25/Feb/2008:08:02:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [25/Feb/2008:08:04:55 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [25/Feb/2008:08:04:56 -0600] "GET / HTTP/1.1" 200 4447 +84.237.120.134 - - [25/Feb/2008:08:05:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +59.124.114.4 - - [25/Feb/2008:08:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +59.124.114.4 - - [25/Feb/2008:08:05:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +59.124.114.4 - - [25/Feb/2008:08:06:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +71.57.91.136 - - [25/Feb/2008:08:07:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 121887 +71.57.91.136 - - [25/Feb/2008:08:07:49 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 +203.109.126.184 - - [25/Feb/2008:08:08:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +203.109.126.184 - - [25/Feb/2008:08:08:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +75.22.21.146 - - [25/Feb/2008:08:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:08:09:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.124.114.4 - - [25/Feb/2008:08:14:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +72.14.220.136 - - [25/Feb/2008:08:15:44 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.195.66.68 - - [25/Feb/2008:08:16:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +72.14.220.136 - - [25/Feb/2008:08:16:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +72.14.220.136 - - [25/Feb/2008:08:16:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +72.14.220.136 - - [25/Feb/2008:08:17:01 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.195.66.68 - - [25/Feb/2008:08:17:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +84.20.132.177 - - [25/Feb/2008:08:18:06 -0600] "GET /ply HTTP/1.1" 301 242 +84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.20.132.177 - - [25/Feb/2008:08:18:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.20.132.177 - - [25/Feb/2008:08:18:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.20.132.177 - - [25/Feb/2008:08:18:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.158 - - [25/Feb/2008:08:18:19 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 +217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.153.4.50 - - [25/Feb/2008:08:18:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.153.4.50 - - [25/Feb/2008:08:18:39 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +217.153.4.50 - - [25/Feb/2008:08:18:40 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +200.195.66.68 - - [25/Feb/2008:08:18:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +217.153.4.50 - - [25/Feb/2008:08:18:44 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +84.20.132.177 - - [25/Feb/2008:08:18:50 -0600] "GET /ply/README HTTP/1.1" 200 8605 +200.195.66.68 - - [25/Feb/2008:08:19:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +200.195.66.68 - - [25/Feb/2008:08:19:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +200.195.66.68 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.0" 200 2290 +217.153.4.50 - - [25/Feb/2008:08:19:46 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 +200.195.66.68 - - [25/Feb/2008:08:19:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.0" 200 4413 +200.195.66.68 - - [25/Feb/2008:08:19:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.0" 200 1641 +200.195.66.68 - - [25/Feb/2008:08:20:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +217.153.4.50 - - [25/Feb/2008:08:20:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 +217.153.4.50 - - [25/Feb/2008:08:20:12 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +66.212.158.132 - - [25/Feb/2008:08:20:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.153.4.50 - - [25/Feb/2008:08:20:57 -0600] "GET /cgi-bin/wiki.pl?CAsAHighLevelLanguage HTTP/1.1" 200 2235 +200.195.66.68 - - [25/Feb/2008:08:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.0" 200 4986 +217.153.4.50 - - [25/Feb/2008:08:21:24 -0600] "GET /cgi-bin/wiki.pl?ConfigurationMemory HTTP/1.1" 200 3793 +217.153.4.50 - - [25/Feb/2008:08:21:40 -0600] "GET /cgi-bin/wiki.pl?TargetLanguageCallbacks HTTP/1.1" 200 3797 +67.195.45.214 - - [25/Feb/2008:08:22:04 -0600] "GET / HTTP/1.0" 200 4447 +200.155.226.207 - - [25/Feb/2008:08:28:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.155.226.207 - - [25/Feb/2008:08:28:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.155.226.207 - - [25/Feb/2008:08:28:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.155.226.207 - - [25/Feb/2008:08:28:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:08:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [25/Feb/2008:08:33:13 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 225488 +64.81.229.55 - - [25/Feb/2008:08:33:14 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246552 +82.195.186.41 - - [25/Feb/2008:08:33:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +82.195.186.41 - - [25/Feb/2008:08:33:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.65.240.234 - - [25/Feb/2008:08:33:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 +217.65.240.234 - - [25/Feb/2008:08:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +216.72.46.162 - - [25/Feb/2008:08:34:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.72.46.162 - - [25/Feb/2008:08:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.72.46.162 - - [25/Feb/2008:08:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [25/Feb/2008:08:35:03 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 +128.221.197.20 - - [25/Feb/2008:08:37:40 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +192.35.17.30 - - [25/Feb/2008:08:37:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.35.17.30 - - [25/Feb/2008:08:38:00 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +220.227.29.99 - - [25/Feb/2008:08:39:52 -0600] "GET /python.html HTTP/1.0" 200 18870 +220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +220.227.29.99 - - [25/Feb/2008:08:39:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.227.29.99 - - [25/Feb/2008:08:40:16 -0600] "GET /training.html HTTP/1.0" 200 6154 +220.227.29.99 - - [25/Feb/2008:08:40:24 -0600] "GET /software.html HTTP/1.0" 200 3163 +220.227.29.99 - - [25/Feb/2008:08:40:28 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +59.124.114.4 - - [25/Feb/2008:08:41:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +59.124.114.4 - - [25/Feb/2008:08:42:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +59.124.114.4 - - [25/Feb/2008:08:42:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +66.232.113.62 - - [25/Feb/2008:08:51:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +38.101.222.130 - - [25/Feb/2008:08:51:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +217.172.56.49 - - [25/Feb/2008:08:51:55 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +62.197.78.103 - - [25/Feb/2008:09:01:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.197.78.103 - - [25/Feb/2008:09:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.197.78.103 - - [25/Feb/2008:09:01:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +61.135.166.102 - - [25/Feb/2008:09:04:23 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [25/Feb/2008:09:07:34 -0600] "GET / HTTP/1.1" 200 4447 +132.207.44.190 - - [25/Feb/2008:09:13:55 -0600] "GET /ply/ HTTP/1.1" 304 - +132.207.44.190 - - [25/Feb/2008:09:14:13 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.183.55.2 - - [25/Feb/2008:09:14:31 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +24.15.187.198 - - [25/Feb/2008:09:19:44 -0600] "GET /dynamic/ HTTP/1.1" 200 5105 +24.15.187.198 - - [25/Feb/2008:09:19:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +213.186.249.190 - - [25/Feb/2008:09:20:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.186.249.190 - - [25/Feb/2008:09:20:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.186.249.190 - - [25/Feb/2008:09:20:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.186.249.190 - - [25/Feb/2008:09:20:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.206.100.135 - - [25/Feb/2008:09:22:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +130.206.100.135 - - [25/Feb/2008:09:22:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +130.206.100.135 - - [25/Feb/2008:09:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.0" 200 9795 +190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.24.202.82 - - [25/Feb/2008:09:25:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.24.202.82 - - [25/Feb/2008:09:25:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:09:29:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.113.44.27 - - [25/Feb/2008:09:30:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:09:30:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.113.44.27 - - [25/Feb/2008:09:30:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +150.210.155.167 - - [25/Feb/2008:09:30:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +91.113.44.27 - - [25/Feb/2008:09:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +130.206.100.135 - - [25/Feb/2008:09:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +91.113.44.27 - - [25/Feb/2008:09:31:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +132.207.44.190 - - [25/Feb/2008:09:32:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.178.179.132 - - [25/Feb/2008:09:33:00 -0600] "GET /python.html HTTP/1.1" 200 18870 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.178.179.132 - - [25/Feb/2008:09:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.178.179.132 - - [25/Feb/2008:09:33:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:09:35:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:09:35:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.237.142.6 - - [25/Feb/2008:09:35:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.237.142.6 - - [25/Feb/2008:09:35:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +67.195.44.107 - - [25/Feb/2008:09:37:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [25/Feb/2008:09:37:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.221.197.20 - - [25/Feb/2008:09:38:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +82.245.141.168 - - [25/Feb/2008:09:39:06 -0600] "GET /cv.html HTTP/1.1" 200 31798 +209.85.136.136 - - [25/Feb/2008:09:39:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +194.237.142.6 - - [25/Feb/2008:09:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.85.136.136 - - [25/Feb/2008:09:39:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +194.237.142.6 - - [25/Feb/2008:09:39:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +81.48.212.152 - - [25/Feb/2008:09:39:34 -0600] "GET /ply/ HTTP/1.1" 304 - +74.6.26.119 - - [25/Feb/2008:09:39:43 -0600] "GET /ply/README HTTP/1.0" 200 8605 +132.207.44.190 - - [25/Feb/2008:09:42:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 +83.103.98.38 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [25/Feb/2008:09:43:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.237.142.6 - - [25/Feb/2008:09:43:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +137.226.57.203 - - [25/Feb/2008:09:43:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +194.237.142.6 - - [25/Feb/2008:09:43:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +137.226.57.203 - - [25/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +137.226.57.203 - - [25/Feb/2008:09:43:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +81.48.212.152 - - [25/Feb/2008:09:45:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.48.212.152 - - [25/Feb/2008:09:46:03 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173064 +81.48.212.152 - - [25/Feb/2008:09:46:08 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 173271 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET / HTTP/1.1" 200 4447 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.181.110 - - [25/Feb/2008:09:48:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +128.135.181.110 - - [25/Feb/2008:09:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.181.110 - - [25/Feb/2008:09:48:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.181.110 - - [25/Feb/2008:09:48:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.114.62.34 - - [25/Feb/2008:09:50:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.114.62.34 - - [25/Feb/2008:09:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:50:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.114.62.34 - - [25/Feb/2008:09:51:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.48.71.175 - - [25/Feb/2008:09:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.48.71.175 - - [25/Feb/2008:09:54:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:09:54:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.20.132.177 - - [25/Feb/2008:09:54:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.20.132.177 - - [25/Feb/2008:09:54:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.82 - - [25/Feb/2008:09:56:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE077.HTM HTTP/1.0" 304 - +134.160.173.1 - - [25/Feb/2008:09:57:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.160.173.1 - - [25/Feb/2008:09:57:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:57:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:57:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:09:58:21 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +137.226.57.203 - - [25/Feb/2008:09:58:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +137.226.57.203 - - [25/Feb/2008:09:58:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [25/Feb/2008:09:58:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +134.160.173.1 - - [25/Feb/2008:09:59:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.160.173.1 - - [25/Feb/2008:10:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:10:07:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +91.113.44.27 - - [25/Feb/2008:10:07:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.113.44.27 - - [25/Feb/2008:10:07:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +91.113.44.27 - - [25/Feb/2008:10:07:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +91.113.44.27 - - [25/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCallbacks HTTP/1.1" 200 3411 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +165.82.168.34 - - [25/Feb/2008:10:07:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.113.44.27 - - [25/Feb/2008:10:08:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +91.113.44.27 - - [25/Feb/2008:10:08:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +84.20.132.177 - - [25/Feb/2008:10:11:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:10:11:21 -0600] "HEAD /video/MVI_1516.AVI HTTP/1.1" 404 0 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.218.61 - - [25/Feb/2008:10:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.218.61 - - [25/Feb/2008:10:12:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.26.231.162 - - [25/Feb/2008:10:13:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:13:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.26.231.162 - - [25/Feb/2008:10:15:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +65.214.44.29 - - [25/Feb/2008:10:16:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.26.231.162 - - [25/Feb/2008:10:17:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +131.44.121.252 - - [25/Feb/2008:10:18:30 -0600] "GET /python.html HTTP/1.1" 200 18870 +131.44.121.252 - - [25/Feb/2008:10:18:31 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply HTTP/1.1" 301 242 +213.157.91.96 - - [25/Feb/2008:10:19:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.91.96 - - [25/Feb/2008:10:19:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.237.142.6 - - [25/Feb/2008:10:29:04 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.237.142.6 - - [25/Feb/2008:10:29:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.221.197.20 - - [25/Feb/2008:10:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +194.237.142.6 - - [25/Feb/2008:10:29:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.63.152.218 - - [25/Feb/2008:10:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.63.152.218 - - [25/Feb/2008:10:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.63.152.218 - - [25/Feb/2008:10:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +71.63.152.218 - - [25/Feb/2008:10:30:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +128.135.11.245 - - [25/Feb/2008:10:32:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.247.172.8 - - [25/Feb/2008:10:33:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.30.226.134 - - [25/Feb/2008:10:36:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.22.104.18 - - [25/Feb/2008:10:38:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.145.39.193 - - [25/Feb/2008:10:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [25/Feb/2008:10:41:08 -0600] "GET /ply/ HTTP/1.0" 304 - +82.166.58.226 - - [25/Feb/2008:10:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [25/Feb/2008:10:49:54 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +24.7.210.64 - - [25/Feb/2008:10:54:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [25/Feb/2008:10:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:10:55:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +196.25.255.246 - - [25/Feb/2008:10:56:18 -0600] "GET /ply/ HTTP/1.1" 304 - +82.239.61.147 - - [25/Feb/2008:10:59:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:10:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.7.210.64 - - [25/Feb/2008:11:01:19 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [25/Feb/2008:11:01:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.151.244.16 - - [25/Feb/2008:11:06:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:06:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.128.56.113 - - [25/Feb/2008:11:07:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +190.128.56.113 - - [25/Feb/2008:11:07:53 -0600] "GET /ply/index.html HTTP/1.1" 304 - +190.128.56.113 - - [25/Feb/2008:11:07:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.128.56.113 - - [25/Feb/2008:11:08:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.7.113 - - [25/Feb/2008:11:15:25 -0600] "GET /photos/wind/pages/IMG_1298.htm HTTP/1.0" 404 133 +38.104.0.30 - - [25/Feb/2008:11:27:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [25/Feb/2008:11:27:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +169.137.105.115 - - [25/Feb/2008:11:29:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +169.137.105.115 - - [25/Feb/2008:11:29:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +169.137.105.115 - - [25/Feb/2008:11:29:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +169.137.105.115 - - [25/Feb/2008:11:29:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /ply/ HTTP/1.1" 304 - +212.240.156.251 - - [25/Feb/2008:11:31:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.240.156.251 - - [25/Feb/2008:11:31:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:11:33:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.67.152.241 - - [25/Feb/2008:11:41:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.67.152.241 - - [25/Feb/2008:11:41:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.67.152.241 - - [25/Feb/2008:11:41:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.151.244.16 - - [25/Feb/2008:11:43:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +208.22.104.18 - - [25/Feb/2008:11:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +80.229.34.140 - - [25/Feb/2008:11:50:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.229.34.140 - - [25/Feb/2008:11:50:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:11:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:02:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.164.194 - - [25/Feb/2008:12:03:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +170.252.64.1 - - [25/Feb/2008:12:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +170.252.64.1 - - [25/Feb/2008:12:04:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +128.135.164.194 - - [25/Feb/2008:12:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.194 - - [25/Feb/2008:12:05:01 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +170.252.64.1 - - [25/Feb/2008:12:06:16 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +170.252.64.1 - - [25/Feb/2008:12:06:27 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +170.252.64.1 - - [25/Feb/2008:12:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +170.252.64.1 - - [25/Feb/2008:12:06:41 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +128.135.11.245 - - [25/Feb/2008:12:07:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:12:08:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.30.84.93 - - [25/Feb/2008:12:15:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.72.97.51 - - [25/Feb/2008:12:35:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.72.97.51 - - [25/Feb/2008:12:35:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.72.97.51 - - [25/Feb/2008:12:36:27 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +68.72.97.51 - - [25/Feb/2008:12:36:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +70.137.112.2 - - [25/Feb/2008:12:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.194.136.18 - - [25/Feb/2008:12:37:19 -0600] "GET /ply HTTP/1.1" 301 242 +128.135.139.146 - - [25/Feb/2008:12:40:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.139.146 - - [25/Feb/2008:12:41:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +64.46.248.1 - - [25/Feb/2008:12:43:00 -0600] "GET / HTTP/1.0" 200 4447 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 60025 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.46.248.1 - - [25/Feb/2008:12:43:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /python.html HTTP/1.0" 200 18870 +64.46.248.1 - - [25/Feb/2008:12:43:04 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +64.46.248.1 - - [25/Feb/2008:12:43:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.81.80.193 - - [25/Feb/2008:12:46:39 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 186488 +71.57.91.136 - - [25/Feb/2008:12:55:11 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 246555 +71.57.91.136 - - [25/Feb/2008:12:55:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:12:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:12:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [25/Feb/2008:12:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [25/Feb/2008:13:01:04 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +71.170.119.34 - - [25/Feb/2008:13:01:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.170.119.34 - - [25/Feb/2008:13:03:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.234.185.130 - - [25/Feb/2008:13:04:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.234.185.130 - - [25/Feb/2008:13:04:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [25/Feb/2008:13:05:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.23.48 - - [25/Feb/2008:13:05:09 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1331 +209.234.185.130 - - [25/Feb/2008:13:05:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +83.23.227.219 - - [25/Feb/2008:13:06:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.23.227.219 - - [25/Feb/2008:13:06:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +206.75.15.14 - - [25/Feb/2008:13:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.75.15.14 - - [25/Feb/2008:13:06:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1400 +206.75.15.14 - - [25/Feb/2008:13:06:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1026 +128.221.197.20 - - [25/Feb/2008:13:06:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +208.163.53.37 - - [25/Feb/2008:13:09:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +208.163.53.37 - - [25/Feb/2008:13:09:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.171.251 - - [25/Feb/2008:13:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.122.155.30 - - [25/Feb/2008:13:11:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.122.155.30 - - [25/Feb/2008:13:11:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.122.155.30 - - [25/Feb/2008:13:12:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +200.122.155.30 - - [25/Feb/2008:13:12:12 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +208.163.53.37 - - [25/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.163.53.37 - - [25/Feb/2008:13:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.163.53.37 - - [25/Feb/2008:13:17:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.188 - - [25/Feb/2008:13:22:37 -0600] "GET /training.html HTTP/1.0" 304 - +82.73.225.225 - - [25/Feb/2008:13:22:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.73.225.225 - - [25/Feb/2008:13:22:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.73.225.225 - - [25/Feb/2008:13:22:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.73.225.225 - - [25/Feb/2008:13:22:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.195.58.186 - - [25/Feb/2008:13:23:06 -0600] "GET /about.html HTTP/1.0" 304 - +202.160.174.4 - - [25/Feb/2008:13:23:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +202.160.174.4 - - [25/Feb/2008:13:23:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [25/Feb/2008:13:23:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +67.195.58.165 - - [25/Feb/2008:13:23:18 -0600] "GET /writing.html HTTP/1.0" 304 - +81.52.143.16 - - [25/Feb/2008:13:25:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +82.239.61.147 - - [25/Feb/2008:13:25:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.52.143.16 - - [25/Feb/2008:13:25:58 -0600] "GET / HTTP/1.1" 200 4447 +82.239.61.147 - - [25/Feb/2008:13:26:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:13:26:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.77 - - [25/Feb/2008:13:29:39 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1510 +82.239.61.147 - - [25/Feb/2008:13:30:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:13:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +41.222.70.189 - - [25/Feb/2008:13:36:05 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 200 21384 +41.222.70.189 - - [25/Feb/2008:13:36:55 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.0" 206 3108482 +200.122.155.30 - - [25/Feb/2008:13:41:03 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +200.122.155.30 - - [25/Feb/2008:13:41:11 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +71.57.91.136 - - [25/Feb/2008:13:43:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.203.10.71 - - [25/Feb/2008:13:43:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.194 - - [25/Feb/2008:13:48:38 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +219.93.175.69 - - [25/Feb/2008:13:48:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +200.13.243.76 - - [25/Feb/2008:13:48:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +190.30.84.93 - - [25/Feb/2008:13:52:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.221.197.20 - - [25/Feb/2008:13:52:35 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +62.121.64.81 - - [25/Feb/2008:13:52:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:52:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:53:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.121.64.81 - - [25/Feb/2008:13:53:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +62.121.64.81 - - [25/Feb/2008:13:53:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +62.121.64.81 - - [25/Feb/2008:13:53:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 +62.121.64.81 - - [25/Feb/2008:13:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +85.194.45.170 - - [25/Feb/2008:14:04:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.194.45.170 - - [25/Feb/2008:14:04:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [25/Feb/2008:14:05:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.31.165 - - [25/Feb/2008:14:05:19 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [25/Feb/2008:14:05:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 +85.194.45.170 - - [25/Feb/2008:14:07:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.194.45.170 - - [25/Feb/2008:14:09:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.192.69.3 - - [25/Feb/2008:14:09:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +206.192.69.3 - - [25/Feb/2008:14:10:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +204.154.183.65 - - [25/Feb/2008:14:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [25/Feb/2008:14:10:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.150 - - [25/Feb/2008:14:11:32 -0600] "GET /publications.html HTTP/1.0" 200 7758 +204.154.183.65 - - [25/Feb/2008:14:11:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.171 - - [25/Feb/2008:14:11:57 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE082.HTM HTTP/1.0" 200 1526 +199.171.86.151 - - [25/Feb/2008:14:12:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +199.171.86.151 - - [25/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +199.171.86.151 - - [25/Feb/2008:14:12:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +199.171.86.151 - - [25/Feb/2008:14:13:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Io HTTP/1.1" 200 1421 +199.171.86.151 - - [25/Feb/2008:14:13:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +74.6.31.151 - - [25/Feb/2008:14:14:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +204.154.183.65 - - [25/Feb/2008:14:15:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.248.57.218 - - [25/Feb/2008:14:18:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.248.57.218 - - [25/Feb/2008:14:18:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.138.87.130 - - [25/Feb/2008:14:20:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.80.193.52 - - [25/Feb/2008:14:26:08 -0600] "GET /cgi-bin/wiki.pl?action=browse&diff=1&id=swigfaq/sharedlibraries HTTP/1.1" 200 1528 +85.194.45.170 - - [25/Feb/2008:14:27:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:28:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:29:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.132.99.224 - - [25/Feb/2008:14:29:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.132.99.224 - - [25/Feb/2008:14:29:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.132.99.224 - - [25/Feb/2008:14:29:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +80.132.99.224 - - [25/Feb/2008:14:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.132.99.224 - - [25/Feb/2008:14:29:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +80.132.99.224 - - [25/Feb/2008:14:29:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +208.22.104.18 - - [25/Feb/2008:14:31:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.22.104.18 - - [25/Feb/2008:14:31:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.192.69.2 - - [25/Feb/2008:14:32:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [25/Feb/2008:14:32:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +208.22.104.18 - - [25/Feb/2008:14:32:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +88.114.145.139 - - [25/Feb/2008:14:34:38 -0600] "GET /python.html HTTP/1.1" 200 18870 +88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +88.114.145.139 - - [25/Feb/2008:14:34:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12634 +62.224.58.211 - - [25/Feb/2008:14:38:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.224.58.211 - - [25/Feb/2008:14:38:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.224.58.211 - - [25/Feb/2008:14:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.69.221.130 - - [25/Feb/2008:14:38:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +128.135.194.138 - - [25/Feb/2008:14:45:12 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +128.135.194.138 - - [25/Feb/2008:14:45:19 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +81.52.143.15 - - [25/Feb/2008:14:45:37 -0600] "GET /robots.txt HTTP/1.1" 200 71 +81.52.143.15 - - [25/Feb/2008:14:45:47 -0600] "GET /ply HTTP/1.1" 301 242 +128.135.194.138 - - [25/Feb/2008:14:45:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.110.251.215 - - [25/Feb/2008:14:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.110.251.215 - - [25/Feb/2008:14:45:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.241.68.226 - - [25/Feb/2008:14:49:22 -0600] "GET / HTTP/1.1" 200 4447 +66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +66.241.68.226 - - [25/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.241.68.226 - - [25/Feb/2008:14:50:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +66.241.68.226 - - [25/Feb/2008:14:50:08 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +66.241.68.226 - - [25/Feb/2008:14:51:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 +66.241.68.226 - - [25/Feb/2008:14:51:47 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +66.241.68.226 - - [25/Feb/2008:14:52:14 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +66.241.68.226 - - [25/Feb/2008:14:53:42 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +66.241.68.226 - - [25/Feb/2008:14:54:41 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +66.241.68.226 - - [25/Feb/2008:14:54:44 -0600] "GET /diet.html HTTP/1.1" 404 133 +200.171.34.14 - - [25/Feb/2008:14:55:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +200.171.34.14 - - [25/Feb/2008:14:55:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.216 - - [25/Feb/2008:14:55:46 -0600] "GET / HTTP/1.0" 304 - +80.132.99.224 - - [25/Feb/2008:14:56:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +128.221.197.20 - - [25/Feb/2008:15:01:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +70.137.112.2 - - [25/Feb/2008:15:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.34.14 - - [25/Feb/2008:15:04:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +200.171.34.14 - - [25/Feb/2008:15:04:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5105 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:15:08:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:15:09:02 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +129.106.32.126 - - [25/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.106.32.126 - - [25/Feb/2008:15:10:41 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1827 +129.106.32.126 - - [25/Feb/2008:15:10:44 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 +129.106.32.126 - - [25/Feb/2008:15:10:58 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +129.106.32.126 - - [25/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +129.106.32.126 - - [25/Feb/2008:15:11:04 -0600] "GET /cgi-bin/wiki.pl?InlineDirective HTTP/1.1" 200 2103 +129.106.32.126 - - [25/Feb/2008:15:11:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +129.106.32.126 - - [25/Feb/2008:15:11:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +129.106.32.126 - - [25/Feb/2008:15:11:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +129.106.32.126 - - [25/Feb/2008:15:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +129.106.32.126 - - [25/Feb/2008:15:11:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +129.106.32.126 - - [25/Feb/2008:15:11:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:14:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.83.72.201 - - [25/Feb/2008:15:14:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +160.83.72.201 - - [25/Feb/2008:15:14:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +160.83.72.201 - - [25/Feb/2008:15:14:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 +89.142.106.15 - - [25/Feb/2008:15:16:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +89.142.106.15 - - [25/Feb/2008:15:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.142.106.15 - - [25/Feb/2008:15:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +89.142.106.15 - - [25/Feb/2008:15:16:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +89.142.106.15 - - [25/Feb/2008:15:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +160.83.72.201 - - [25/Feb/2008:15:17:13 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 1167 +74.6.19.115 - - [25/Feb/2008:15:17:56 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [25/Feb/2008:15:17:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +62.224.58.211 - - [25/Feb/2008:15:18:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.224.58.211 - - [25/Feb/2008:15:18:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:20:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +160.83.72.201 - - [25/Feb/2008:15:20:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +160.83.72.201 - - [25/Feb/2008:15:21:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +160.83.72.201 - - [25/Feb/2008:15:21:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +74.6.26.100 - - [25/Feb/2008:15:22:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.0" 200 2184 +160.83.72.201 - - [25/Feb/2008:15:22:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +208.22.104.18 - - [25/Feb/2008:15:24:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +86.205.155.128 - - [25/Feb/2008:15:24:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.205.155.128 - - [25/Feb/2008:15:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.205.155.128 - - [25/Feb/2008:15:24:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [25/Feb/2008:15:25:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:15:39:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.239.61.147 - - [25/Feb/2008:15:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.239.61.147 - - [25/Feb/2008:15:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [25/Feb/2008:15:39:48 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.35.206.246 - - [25/Feb/2008:15:50:02 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +82.35.206.246 - - [25/Feb/2008:15:50:09 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +82.35.206.246 - - [25/Feb/2008:15:50:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +82.35.206.246 - - [25/Feb/2008:15:50:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.35.206.246 - - [25/Feb/2008:15:50:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +82.35.206.246 - - [25/Feb/2008:15:51:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.35.206.246 - - [25/Feb/2008:15:55:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.35.206.246 - - [25/Feb/2008:15:55:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +203.166.87.218 - - [25/Feb/2008:15:58:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +203.166.87.218 - - [25/Feb/2008:15:58:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +203.166.87.218 - - [25/Feb/2008:15:59:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=SwigFaqDLLForWindows/Msys HTTP/1.1" 200 1927 +203.166.87.218 - - [25/Feb/2008:15:59:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic HTTP/1.1" 301 246 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +68.252.247.8 - - [25/Feb/2008:16:03:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.247.8 - - [25/Feb/2008:16:03:09 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +68.252.247.8 - - [25/Feb/2008:16:03:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.13.234 - - [25/Feb/2008:16:09:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +76.223.13.234 - - [25/Feb/2008:16:09:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +88.191.19.81 - - [25/Feb/2008:16:09:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.157.119.197 - - [25/Feb/2008:16:11:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.91.134.210 - - [25/Feb/2008:16:23:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.134.210 - - [25/Feb/2008:16:23:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.204.240.53 - - [25/Feb/2008:16:23:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.204.240.53 - - [25/Feb/2008:16:23:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.91.134.210 - - [25/Feb/2008:16:23:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +140.160.129.28 - - [25/Feb/2008:16:25:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.28.166 - - [25/Feb/2008:16:27:33 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1346 +69.91.134.210 - - [25/Feb/2008:16:31:21 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.113.40.1 - - [25/Feb/2008:16:31:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET / HTTP/1.1" 200 4447 +128.135.24.9 - - [25/Feb/2008:16:36:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.24.9 - - [25/Feb/2008:16:36:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.24.9 - - [25/Feb/2008:16:36:48 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +140.160.129.28 - - [25/Feb/2008:16:36:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.74.95.23 - - [25/Feb/2008:16:36:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.74.95.23 - - [25/Feb/2008:16:37:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [25/Feb/2008:16:37:16 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.135.24.9 - - [25/Feb/2008:16:37:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +87.74.95.23 - - [25/Feb/2008:16:38:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.102.138.111 - - [25/Feb/2008:16:39:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [25/Feb/2008:16:40:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +140.160.199.105 - - [25/Feb/2008:16:48:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +140.160.199.105 - - [25/Feb/2008:16:49:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.160.199.105 - - [25/Feb/2008:16:49:10 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +87.74.95.23 - - [25/Feb/2008:16:51:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.74.95.23 - - [25/Feb/2008:16:51:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET / HTTP/1.0" 304 - +200.21.98.7 - - [25/Feb/2008:16:58:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.21.98.7 - - [25/Feb/2008:16:58:42 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +200.21.98.7 - - [25/Feb/2008:16:58:56 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 494 +210.143.35.13 - - [25/Feb/2008:16:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.21.98.7 - - [25/Feb/2008:16:59:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 206 133088 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 +204.246.129.196 - - [25/Feb/2008:17:01:39 -0600] "GET /python.html HTTP/1.1" 200 18870 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +204.253.252.21 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.253.252.20 - - [25/Feb/2008:17:01:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.189.175 - - [25/Feb/2008:17:03:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.175 - - [25/Feb/2008:17:03:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1002 +200.21.98.7 - - [25/Feb/2008:17:05:37 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 43106 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET / HTTP/1.1" 200 4447 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.173.205.76 - - [25/Feb/2008:17:07:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.191.75 - - [25/Feb/2008:17:10:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.191.75 - - [25/Feb/2008:17:10:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 987 +81.48.212.152 - - [25/Feb/2008:17:11:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +190.139.116.130 - - [25/Feb/2008:17:15:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 +76.213.231.36 - - [25/Feb/2008:17:16:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.213.231.36 - - [25/Feb/2008:17:16:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.213.231.36 - - [25/Feb/2008:17:16:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +210.143.35.13 - - [25/Feb/2008:17:18:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.143.35.13 - - [25/Feb/2008:17:18:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.172.156.57 - - [25/Feb/2008:17:23:23 -0600] "GET /python.html HTTP/1.1" 200 18870 +58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +58.172.156.57 - - [25/Feb/2008:17:23:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.187.74 - - [25/Feb/2008:17:24:54 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [25/Feb/2008:17:24:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +210.143.35.13 - - [25/Feb/2008:17:25:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20232 +88.191.19.81 - - [25/Feb/2008:17:27:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +208.49.99.11 - - [25/Feb/2008:17:29:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.49.99.11 - - [25/Feb/2008:17:30:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.49.99.11 - - [25/Feb/2008:17:30:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +41.221.16.105 - - [25/Feb/2008:17:34:09 -0600] "GET / HTTP/1.1" 200 4447 +41.221.16.105 - - [25/Feb/2008:17:34:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [25/Feb/2008:17:35:53 -0600] "GET / HTTP/1.1" 200 4447 +220.194.55.45 - - [25/Feb/2008:17:42:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.135.24.9 - - [25/Feb/2008:17:48:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +75.58.86.1 - - [25/Feb/2008:17:48:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:17:50:45 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:17:50:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:17:50:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:17:50:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:17:51:19 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.143.231.202 - - [25/Feb/2008:17:53:51 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [25/Feb/2008:17:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.154 - - [25/Feb/2008:18:00:43 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.0" 200 3246437 +85.160.19.27 - - [25/Feb/2008:18:02:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +85.160.19.27 - - [25/Feb/2008:18:02:47 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.160.19.27 - - [25/Feb/2008:18:02:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:18:03:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.17.146.129 - - [25/Feb/2008:18:03:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +209.17.146.129 - - [25/Feb/2008:18:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:18:03:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.15.126.192 - - [25/Feb/2008:18:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:04:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.15.126.192 - - [25/Feb/2008:18:04:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1381 +216.15.126.192 - - [25/Feb/2008:18:04:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 +121.139.76.33 - - [25/Feb/2008:18:09:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +121.139.76.33 - - [25/Feb/2008:18:09:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 +121.139.76.33 - - [25/Feb/2008:18:09:58 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/SwigInPython HTTP/1.1" 200 1704 +75.58.86.1 - - [25/Feb/2008:18:13:56 -0600] "GET /dynamic/ HTTP/1.1" 304 - +75.58.86.1 - - [25/Feb/2008:18:13:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.195.58.174 - - [25/Feb/2008:18:14:23 -0600] "GET /ply/ HTTP/1.0" 304 - +128.135.24.238 - - [25/Feb/2008:18:14:25 -0600] "GET /dynamic/ffcache.zip HTTP/1.0" 200 4919642 +67.195.58.170 - - [25/Feb/2008:18:14:58 -0600] "GET / HTTP/1.0" 200 4447 +67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/README HTTP/1.0" 200 8605 +67.195.58.158 - - [25/Feb/2008:18:15:32 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 200 64742 +67.195.58.158 - - [25/Feb/2008:18:15:33 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +85.160.19.27 - - [25/Feb/2008:18:15:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:18:21:58 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +74.6.31.145 - - [25/Feb/2008:18:23:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.144.143.13 - - [25/Feb/2008:18:29:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.144.143.13 - - [25/Feb/2008:18:29:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.60.229.125 - - [25/Feb/2008:18:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.134.126 - - [25/Feb/2008:18:46:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.134.126 - - [25/Feb/2008:18:46:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +61.135.190.17 - - [25/Feb/2008:18:51:13 -0600] "GET /ply/ HTTP/1.1" 304 - +85.160.19.27 - - [25/Feb/2008:18:55:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:18:58:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.231.169 - - [25/Feb/2008:18:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:18:59:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.17.146.129 - - [25/Feb/2008:19:03:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.231.169 - - [25/Feb/2008:19:04:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [25/Feb/2008:19:10:02 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1265 +130.240.203.74 - - [25/Feb/2008:19:10:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.231.169 - - [25/Feb/2008:19:10:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.240.203.74 - - [25/Feb/2008:19:10:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.240.203.74 - - [25/Feb/2008:19:10:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +70.245.137.240 - - [25/Feb/2008:19:15:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.137.240 - - [25/Feb/2008:19:15:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.137.240 - - [25/Feb/2008:19:15:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.249.206.181 - - [25/Feb/2008:19:17:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:17:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +24.249.206.181 - - [25/Feb/2008:19:17:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +24.249.206.181 - - [25/Feb/2008:19:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +128.143.231.169 - - [25/Feb/2008:19:18:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.249.206.181 - - [25/Feb/2008:19:18:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +24.249.206.181 - - [25/Feb/2008:19:19:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +24.249.206.181 - - [25/Feb/2008:19:20:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +202.179.180.52 - - [25/Feb/2008:19:20:29 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [25/Feb/2008:19:20:30 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 +128.143.231.169 - - [25/Feb/2008:19:28:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.3.94.70 - - [25/Feb/2008:19:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.169.3 - - [25/Feb/2008:19:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply HTTP/1.1" 301 242 +82.11.31.114 - - [25/Feb/2008:19:31:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.169.3 - - [25/Feb/2008:19:32:11 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.99.169.3 - - [25/Feb/2008:19:32:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.99.169.3 - - [25/Feb/2008:19:32:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 +74.6.26.22 - - [25/Feb/2008:19:32:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 +98.224.230.125 - - [25/Feb/2008:19:34:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +98.224.230.125 - - [25/Feb/2008:19:34:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.224.230.125 - - [25/Feb/2008:19:35:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +98.224.230.125 - - [25/Feb/2008:19:35:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +98.224.230.125 - - [25/Feb/2008:19:36:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +98.224.230.125 - - [25/Feb/2008:19:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +74.6.19.237 - - [25/Feb/2008:19:38:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 +74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.8.73 - - [25/Feb/2008:19:41:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +71.3.94.70 - - [25/Feb/2008:19:45:21 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +69.113.211.8 - - [25/Feb/2008:19:50:03 -0600] "GET /ply/ HTTP/1.0" 200 8018 +69.113.211.8 - - [25/Feb/2008:19:50:04 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +128.143.231.169 - - [25/Feb/2008:19:51:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [25/Feb/2008:19:54:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.148.212.222 - - [25/Feb/2008:19:54:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [25/Feb/2008:19:54:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.148.212.222 - - [25/Feb/2008:19:54:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +216.148.212.222 - - [25/Feb/2008:19:58:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.148.212.222 - - [25/Feb/2008:19:58:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +129.82.46.120 - - [25/Feb/2008:20:02:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.186.98.20 - - [25/Feb/2008:20:06:04 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 628284 +67.186.98.20 - - [25/Feb/2008:20:06:12 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 2935451 +67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [25/Feb/2008:20:06:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [25/Feb/2008:20:07:26 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.186.98.20 - - [25/Feb/2008:20:12:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 199044 +67.186.98.20 - - [25/Feb/2008:20:12:28 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 +216.148.212.222 - - [25/Feb/2008:20:12:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +148.241.82.6 - - [25/Feb/2008:20:12:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.82.6 - - [25/Feb/2008:20:12:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.82.6 - - [25/Feb/2008:20:12:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.82.6 - - [25/Feb/2008:20:13:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +129.82.46.120 - - [25/Feb/2008:20:13:22 -0600] "GET /ply/ HTTP/1.1" 304 - +129.82.46.120 - - [25/Feb/2008:20:13:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +193.202.115.177 - - [25/Feb/2008:20:16:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.8.73 - - [25/Feb/2008:20:20:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +71.57.91.136 - - [25/Feb/2008:20:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.36.158.68 - - [25/Feb/2008:20:20:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +69.36.158.68 - - [25/Feb/2008:20:20:35 -0600] "GET / HTTP/1.1" 200 4447 +69.36.158.68 - - [25/Feb/2008:20:20:36 -0600] "GET /index.html HTTP/1.1" 200 4447 +69.36.158.68 - - [25/Feb/2008:20:20:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +69.36.158.68 - - [25/Feb/2008:20:20:38 -0600] "GET /software.html HTTP/1.1" 200 3163 +69.36.158.68 - - [25/Feb/2008:20:20:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +69.36.158.68 - - [25/Feb/2008:20:20:40 -0600] "GET /writing.html HTTP/1.1" 200 2871 +69.36.158.68 - - [25/Feb/2008:20:20:41 -0600] "GET /about.html HTTP/1.1" 200 7890 +69.36.158.68 - - [25/Feb/2008:20:20:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.36.158.68 - - [25/Feb/2008:20:20:44 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +69.36.158.68 - - [25/Feb/2008:20:20:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +69.36.158.68 - - [25/Feb/2008:20:20:46 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +69.36.158.68 - - [25/Feb/2008:20:20:47 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +69.36.158.68 - - [25/Feb/2008:20:20:48 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +69.36.158.68 - - [25/Feb/2008:20:20:49 -0600] "GET /publications.html HTTP/1.1" 200 7758 +69.36.158.68 - - [25/Feb/2008:20:20:50 -0600] "GET /cv.html HTTP/1.1" 200 31798 +69.36.158.68 - - [25/Feb/2008:20:20:51 -0600] "GET /diet.html HTTP/1.1" 404 133 +69.36.158.68 - - [25/Feb/2008:20:20:52 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +69.36.158.68 - - [25/Feb/2008:20:20:53 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +69.36.158.68 - - [25/Feb/2008:20:20:54 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +69.36.158.68 - - [25/Feb/2008:20:20:55 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +69.36.158.68 - - [25/Feb/2008:20:20:56 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/ HTTP/1.1" 304 - +148.241.82.6 - - [25/Feb/2008:20:21:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +201.213.27.208 - - [25/Feb/2008:20:22:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.213.27.208 - - [25/Feb/2008:20:22:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.213.27.208 - - [25/Feb/2008:20:22:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.213.27.208 - - [25/Feb/2008:20:22:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.186.98.20 - - [25/Feb/2008:20:28:07 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 250956 +67.186.98.20 - - [25/Feb/2008:20:28:18 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 206 3181210 +71.57.91.136 - - [25/Feb/2008:20:35:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.87.213.22 - - [25/Feb/2008:20:37:26 -0600] "GET / HTTP/1.1" 200 4447 +12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +12.87.213.22 - - [25/Feb/2008:20:37:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +12.87.213.22 - - [25/Feb/2008:20:37:50 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +129.82.46.120 - - [25/Feb/2008:20:38:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +12.87.213.22 - - [25/Feb/2008:20:39:06 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +217.172.44.82 - - [25/Feb/2008:20:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.82.46.120 - - [25/Feb/2008:20:53:39 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:20:55:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:20:55:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +72.85.134.143 - - [25/Feb/2008:20:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.85.134.143 - - [25/Feb/2008:20:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [25/Feb/2008:21:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.160 - - [25/Feb/2008:21:03:47 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE045.HTM HTTP/1.0" 304 - +217.196.43.134 - - [25/Feb/2008:21:05:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.47.57.29 - - [25/Feb/2008:21:09:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.47.57.29 - - [25/Feb/2008:21:09:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.47.57.29 - - [25/Feb/2008:21:09:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:21:09:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +75.22.21.146 - - [25/Feb/2008:21:09:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [25/Feb/2008:21:09:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +75.22.21.146 - - [25/Feb/2008:21:09:59 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [25/Feb/2008:21:14:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.1" 304 - +71.57.91.136 - - [25/Feb/2008:21:19:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [25/Feb/2008:21:19:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.149 - - [25/Feb/2008:21:23:01 -0600] "GET /about.html HTTP/1.0" 200 7890 +75.22.21.146 - - [25/Feb/2008:21:25:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.101 - - [25/Feb/2008:21:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +64.124.85.75 - - [25/Feb/2008:21:36:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.75 - - [25/Feb/2008:21:39:27 -0600] "GET /ply HTTP/1.1" 301 242 +129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.82.47.96 - - [25/Feb/2008:21:41:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +129.82.47.96 - - [25/Feb/2008:21:41:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.82.47.96 - - [25/Feb/2008:21:41:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.232.164.26 - - [25/Feb/2008:21:49:43 -0600] "GET /about.html HTTP/1.0" 200 7890 +38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [25/Feb/2008:22:04:57 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [25/Feb/2008:22:05:01 -0600] "GET / HTTP/1.1" 200 4447 +202.118.7.10 - - [25/Feb/2008:22:08:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +202.118.7.10 - - [25/Feb/2008:22:08:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.118.7.10 - - [25/Feb/2008:22:08:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.118.7.10 - - [25/Feb/2008:22:09:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.191.64.124 - - [25/Feb/2008:22:15:33 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.104.0.30 - - [25/Feb/2008:22:15:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [25/Feb/2008:22:15:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +202.118.7.10 - - [25/Feb/2008:22:17:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.72.123.219 - - [25/Feb/2008:22:18:25 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +74.6.28.76 - - [25/Feb/2008:22:21:28 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1409 +203.200.218.2 - - [25/Feb/2008:22:24:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.200.218.2 - - [25/Feb/2008:22:24:15 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.200.218.2 - - [25/Feb/2008:22:24:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.135.15.12 - - [25/Feb/2008:22:24:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.135.15.12 - - [25/Feb/2008:22:24:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.72 - - [25/Feb/2008:22:25:31 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +203.200.218.2 - - [25/Feb/2008:22:26:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.236.226.90 - - [25/Feb/2008:22:26:38 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:22:26:42 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 15733 +201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [25/Feb/2008:22:26:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [25/Feb/2008:22:26:52 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +129.82.47.96 - - [25/Feb/2008:22:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic HTTP/1.1" 301 246 +66.146.214.212 - - [25/Feb/2008:22:31:13 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +66.146.214.212 - - [25/Feb/2008:22:31:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +129.82.47.96 - - [25/Feb/2008:22:34:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.24 - - [25/Feb/2008:22:36:03 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +66.116.72.114 - - [25/Feb/2008:22:37:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [25/Feb/2008:22:37:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:40:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:40:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.199.14.198 - - [25/Feb/2008:22:40:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.94.167.76 - - [25/Feb/2008:22:41:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.94.167.76 - - [25/Feb/2008:22:41:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +203.94.167.76 - - [25/Feb/2008:22:41:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +76.199.14.198 - - [25/Feb/2008:22:42:42 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +66.146.214.212 - - [25/Feb/2008:22:43:06 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +84.110.229.145 - - [25/Feb/2008:22:45:32 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.229.145 - - [25/Feb/2008:22:45:37 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +202.179.180.45 - - [25/Feb/2008:22:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.45 - - [25/Feb/2008:22:49:11 -0600] "GET / HTTP/1.1" 200 4447 +202.179.180.45 - - [25/Feb/2008:22:49:18 -0600] "GET /index.html HTTP/1.1" 200 4447 +202.179.180.45 - - [25/Feb/2008:22:49:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +202.179.180.45 - - [25/Feb/2008:22:49:30 -0600] "GET /software.html HTTP/1.1" 200 3163 +202.179.180.45 - - [25/Feb/2008:22:49:37 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +202.179.180.45 - - [25/Feb/2008:22:49:43 -0600] "GET /writing.html HTTP/1.1" 200 2871 +202.179.180.45 - - [25/Feb/2008:22:49:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +202.179.180.45 - - [25/Feb/2008:22:49:56 -0600] "GET /python.html HTTP/1.1" 200 18870 +202.179.180.45 - - [25/Feb/2008:22:50:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +202.179.180.45 - - [25/Feb/2008:22:50:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +202.179.180.45 - - [25/Feb/2008:22:50:15 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +202.179.180.45 - - [25/Feb/2008:22:50:21 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +202.179.180.45 - - [25/Feb/2008:22:50:28 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +202.179.180.45 - - [25/Feb/2008:22:50:32 -0600] "GET /publications.html HTTP/1.1" 200 7758 +202.179.180.45 - - [25/Feb/2008:22:50:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 +68.226.202.243 - - [25/Feb/2008:22:50:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.226.202.243 - - [25/Feb/2008:22:51:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.226.202.243 - - [25/Feb/2008:22:51:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +68.226.202.243 - - [25/Feb/2008:22:51:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +58.71.35.197 - - [25/Feb/2008:22:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.71.35.197 - - [25/Feb/2008:22:58:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +58.71.35.197 - - [25/Feb/2008:22:58:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:22:59:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [25/Feb/2008:23:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.182.43.140 - - [25/Feb/2008:23:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.182.43.140 - - [25/Feb/2008:23:11:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.110 - - [25/Feb/2008:23:11:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.20.44 - - [25/Feb/2008:23:15:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 +61.247.217.37 - - [25/Feb/2008:23:16:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +58.71.35.197 - - [25/Feb/2008:23:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.67.135.113 - - [25/Feb/2008:23:24:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +61.67.135.113 - - [25/Feb/2008:23:24:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +24.1.247.118 - - [25/Feb/2008:23:26:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [25/Feb/2008:23:26:21 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +58.71.35.197 - - [25/Feb/2008:23:27:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:27:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.71.35.197 - - [25/Feb/2008:23:28:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [25/Feb/2008:23:28:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE043.HTM HTTP/1.1" 200 1178 +74.125.16.5 - - [25/Feb/2008:23:30:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +63.252.164.2 - - [25/Feb/2008:23:30:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.30.188.110 - - [25/Feb/2008:23:32:35 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +124.30.188.110 - - [25/Feb/2008:23:32:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.118 - - [25/Feb/2008:23:37:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [25/Feb/2008:23:37:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +124.30.188.110 - - [25/Feb/2008:23:39:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.8.73 - - [25/Feb/2008:23:42:11 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.8.73 - - [25/Feb/2008:23:42:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +65.214.45.129 - - [25/Feb/2008:23:43:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 +58.71.35.197 - - [25/Feb/2008:23:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.36.114.239 - - [25/Feb/2008:23:55:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.208.118 - - [25/Feb/2008:23:55:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 +58.71.35.197 - - [25/Feb/2008:23:57:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.115.4.222 - - [25/Feb/2008:23:57:58 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +74.6.26.171 - - [26/Feb/2008:00:00:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.25.81 - - [26/Feb/2008:00:00:44 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +66.249.65.37 - - [26/Feb/2008:00:02:13 -0600] "GET /training.html HTTP/1.1" 304 - +58.71.35.197 - - [26/Feb/2008:00:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [26/Feb/2008:00:04:45 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1310 +75.50.51.97 - - [26/Feb/2008:00:07:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.249.65.37 - - [26/Feb/2008:00:08:43 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - +213.145.165.82 - - [26/Feb/2008:00:16:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.8.73 - - [26/Feb/2008:00:17:23 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - +66.249.65.37 - - [26/Feb/2008:00:23:16 -0600] "GET /ply/example.html HTTP/1.1" 304 - +220.134.105.172 - - [26/Feb/2008:00:23:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.134.105.172 - - [26/Feb/2008:00:23:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.134.105.172 - - [26/Feb/2008:00:25:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.8.211.11 - - [26/Feb/2008:00:25:28 -0600] "GET /python.html HTTP/1.0" 200 18870 +220.134.105.172 - - [26/Feb/2008:00:25:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.8.211.11 - - [26/Feb/2008:00:25:59 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +192.8.211.11 - - [26/Feb/2008:00:26:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.134.105.172 - - [26/Feb/2008:00:26:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +84.110.232.251 - - [26/Feb/2008:00:34:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.232.251 - - [26/Feb/2008:00:34:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1017 +220.134.105.172 - - [26/Feb/2008:00:39:04 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +57.66.144.181 - - [26/Feb/2008:00:55:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.165.73.111 - - [26/Feb/2008:00:58:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +207.225.146.142 - - [26/Feb/2008:00:58:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +207.225.146.142 - - [26/Feb/2008:00:58:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.165.73.111 - - [26/Feb/2008:00:58:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:58:56 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +89.165.73.111 - - [26/Feb/2008:00:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:00:59:17 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +89.165.73.111 - - [26/Feb/2008:01:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:01:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:03:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:01:04:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.111 - - [26/Feb/2008:01:04:18 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 +99.140.232.220 - - [26/Feb/2008:01:04:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [26/Feb/2008:01:05:03 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +66.249.65.37 - - [26/Feb/2008:01:06:05 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 304 - +74.6.8.73 - - [26/Feb/2008:01:06:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +74.6.8.73 - - [26/Feb/2008:01:13:57 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.91.239.214 - - [26/Feb/2008:01:18:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.91.239.214 - - [26/Feb/2008:01:19:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.141.176.22 - - [26/Feb/2008:01:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.141.176.22 - - [26/Feb/2008:01:24:47 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +84.143.76.58 - - [26/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.143.76.58 - - [26/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.143.76.58 - - [26/Feb/2008:01:29:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.115.4.219 - - [26/Feb/2008:01:30:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +66.249.65.37 - - [26/Feb/2008:01:33:15 -0600] "GET /ply/support.html HTTP/1.1" 304 - +85.185.76.213 - - [26/Feb/2008:01:33:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.185.76.213 - - [26/Feb/2008:01:33:49 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.185.76.213 - - [26/Feb/2008:01:38:51 -0600] "GET /ply/ HTTP/1.1" 304 - +85.185.76.213 - - [26/Feb/2008:01:38:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.78.34.166 - - [26/Feb/2008:01:40:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.216.4.6 - - [26/Feb/2008:01:47:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.216.4.6 - - [26/Feb/2008:01:47:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.216.4.6 - - [26/Feb/2008:01:47:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.216.4.6 - - [26/Feb/2008:01:47:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.216.4.6 - - [26/Feb/2008:01:48:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.216.4.6 - - [26/Feb/2008:01:48:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +71.216.4.6 - - [26/Feb/2008:01:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +82.98.238.29 - - [26/Feb/2008:02:02:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.98.238.29 - - [26/Feb/2008:02:02:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.98.238.29 - - [26/Feb/2008:02:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.98.238.29 - - [26/Feb/2008:02:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.170 - - [26/Feb/2008:02:03:41 -0600] "GET /ply/README HTTP/1.1" 200 8605 +84.143.76.58 - - [26/Feb/2008:02:11:02 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.78.34.166 - - [26/Feb/2008:02:11:25 -0600] "GET / HTTP/1.1" 200 4447 +74.6.25.20 - - [26/Feb/2008:02:14:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.174 - - [26/Feb/2008:02:14:32 -0600] "GET /ply/ HTTP/1.0" 304 - +88.191.19.81 - - [26/Feb/2008:02:16:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.78.34.170 - - [26/Feb/2008:02:16:47 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 24524 +213.194.211.50 - - [26/Feb/2008:02:17:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:17:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:02:17:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:02:17:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:17:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.174 - - [26/Feb/2008:02:21:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +202.81.69.153 - - [26/Feb/2008:02:21:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +202.81.69.153 - - [26/Feb/2008:02:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.81.69.153 - - [26/Feb/2008:02:21:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +202.81.69.153 - - [26/Feb/2008:02:22:07 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +72.51.43.159 - - [26/Feb/2008:02:23:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +67.78.34.166 - - [26/Feb/2008:02:27:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.111.46.17 - - [26/Feb/2008:02:27:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +193.111.46.17 - - [26/Feb/2008:02:27:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.111.46.17 - - [26/Feb/2008:02:27:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 +193.111.46.17 - - [26/Feb/2008:02:27:43 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.0" 200 5873 +213.194.211.50 - - [26/Feb/2008:02:29:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.194.211.50 - - [26/Feb/2008:02:30:03 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.78.34.166 - - [26/Feb/2008:02:32:07 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +67.78.34.170 - - [26/Feb/2008:02:36:25 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 12820 +74.6.23.210 - - [26/Feb/2008:02:37:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 +80.99.119.201 - - [26/Feb/2008:02:38:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +88.191.19.81 - - [26/Feb/2008:02:39:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.179.99.175 - - [26/Feb/2008:02:42:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +202.81.69.153 - - [26/Feb/2008:02:42:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +79.179.99.175 - - [26/Feb/2008:02:42:19 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +67.78.34.174 - - [26/Feb/2008:02:42:20 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 12819 +88.131.106.15 - - [26/Feb/2008:02:42:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 +88.131.106.15 - - [26/Feb/2008:02:42:58 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.214.45.129 - - [26/Feb/2008:02:42:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +221.6.82.112 - - [26/Feb/2008:02:43:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.6.82.112 - - [26/Feb/2008:02:43:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +206.51.226.87 - - [26/Feb/2008:02:43:42 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +87.101.244.6 - - [26/Feb/2008:02:43:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +67.207.145.238 - - [26/Feb/2008:02:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +221.6.82.112 - - [26/Feb/2008:02:44:39 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 15480 +221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +221.6.82.112 - - [26/Feb/2008:02:44:48 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +221.6.82.112 - - [26/Feb/2008:02:44:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 206 36955 +221.6.82.112 - - [26/Feb/2008:02:45:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.99.119.201 - - [26/Feb/2008:02:45:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.99.119.201 - - [26/Feb/2008:02:45:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.99.119.201 - - [26/Feb/2008:02:45:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.30.226.4 - - [26/Feb/2008:02:47:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.78.34.170 - - [26/Feb/2008:02:47:27 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 12819 +66.232.113.62 - - [26/Feb/2008:02:47:41 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +80.97.94.178 - - [26/Feb/2008:02:47:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.18.38.242 - - [26/Feb/2008:02:47:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +67.78.34.170 - - [26/Feb/2008:02:52:27 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.1" 200 12819 +83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +83.145.122.242 - - [26/Feb/2008:02:55:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.214.45.129 - - [26/Feb/2008:02:56:29 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 +67.78.34.174 - - [26/Feb/2008:02:58:08 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 12819 +202.81.69.153 - - [26/Feb/2008:03:00:35 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 100406 +202.81.69.153 - - [26/Feb/2008:03:00:39 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161949 +88.191.19.81 - - [26/Feb/2008:03:00:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.78.34.166 - - [26/Feb/2008:03:04:27 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 12819 +71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.62.148.145 - - [26/Feb/2008:03:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [26/Feb/2008:03:04:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.148.145 - - [26/Feb/2008:03:05:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.95.183.252 - - [26/Feb/2008:03:10:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.166 - - [26/Feb/2008:03:11:34 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 12820 +67.78.34.166 - - [26/Feb/2008:03:17:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 12818 +67.78.34.166 - - [26/Feb/2008:03:21:13 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.1" 200 12820 +74.6.23.102 - - [26/Feb/2008:03:23:08 -0600] "GET /index.html HTTP/1.0" 304 - +67.78.34.170 - - [26/Feb/2008:03:24:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 12818 +74.6.8.73 - - [26/Feb/2008:03:25:05 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 +62.171.194.36 - - [26/Feb/2008:03:26:12 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +67.78.34.174 - - [26/Feb/2008:03:29:51 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 12818 +137.226.57.203 - - [26/Feb/2008:03:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:03:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:03:30:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.134.105.172 - - [26/Feb/2008:03:31:25 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +218.94.9.35 - - [26/Feb/2008:03:33:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.94.9.35 - - [26/Feb/2008:03:33:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.94.9.35 - - [26/Feb/2008:03:33:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.94.9.35 - - [26/Feb/2008:03:33:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.165.207.58 - - [26/Feb/2008:03:36:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.207.58 - - [26/Feb/2008:03:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.8.73 - - [26/Feb/2008:03:39:26 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +67.78.34.170 - - [26/Feb/2008:03:43:01 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 12820 +203.123.162.235 - - [26/Feb/2008:03:44:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.123.162.235 - - [26/Feb/2008:03:44:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:44:58 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:03:45:12 -0600] "GET /dynamic/ HTTP/1.1" 304 - +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [26/Feb/2008:03:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.78.34.166 - - [26/Feb/2008:03:53:00 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 200 12819 +75.125.165.10 - - [26/Feb/2008:03:57:36 -0600] "GET / HTTP/1.1" 200 4447 +65.55.208.119 - - [26/Feb/2008:03:58:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [26/Feb/2008:03:58:21 -0600] "GET /swill/Doc/index.html HTTP/1.1" 304 - +128.165.207.58 - - [26/Feb/2008:04:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [26/Feb/2008:04:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.167.100.246 - - [26/Feb/2008:04:05:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +99.167.100.246 - - [26/Feb/2008:04:05:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:04:05:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.100.246 - - [26/Feb/2008:04:05:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +202.81.69.153 - - [26/Feb/2008:04:06:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply HTTP/1.1" 301 242 +192.100.130.8 - - [26/Feb/2008:04:24:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.100.130.8 - - [26/Feb/2008:04:24:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:04:25:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:33:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +160.45.115.176 - - [26/Feb/2008:04:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:33:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +61.57.149.13 - - [26/Feb/2008:04:35:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +61.57.149.13 - - [26/Feb/2008:04:35:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +195.217.226.30 - - [26/Feb/2008:04:44:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.217.226.30 - - [26/Feb/2008:04:44:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.217.226.30 - - [26/Feb/2008:04:44:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 935 +160.45.115.176 - - [26/Feb/2008:04:48:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:48:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +82.239.61.147 - - [26/Feb/2008:04:54:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.239.61.147 - - [26/Feb/2008:04:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:04:54:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +160.45.115.176 - - [26/Feb/2008:04:56:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +160.45.115.176 - - [26/Feb/2008:04:56:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +59.93.91.251 - - [26/Feb/2008:05:00:41 -0600] "GET /python.html HTTP/1.0" 200 18870 +59.93.91.251 - - [26/Feb/2008:05:00:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +59.93.91.251 - - [26/Feb/2008:05:00:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:05:01:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.120.121.126 - - [26/Feb/2008:05:04:01 -0600] "GET /ply/ HTTP/1.0" 200 8018 +220.181.38.169 - - [26/Feb/2008:05:05:24 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:05:07:57 -0600] "GET / HTTP/1.1" 200 4447 +160.45.115.176 - - [26/Feb/2008:05:08:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.93.91.251 - - [26/Feb/2008:05:09:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.25.29 - - [26/Feb/2008:05:09:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +61.135.166.102 - - [26/Feb/2008:05:10:12 -0600] "GET / HTTP/1.1" 200 4447 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.180.137.172 - - [26/Feb/2008:05:12:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.180.137.172 - - [26/Feb/2008:05:12:58 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +213.180.137.172 - - [26/Feb/2008:05:13:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +150.254.74.235 - - [26/Feb/2008:05:16:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 +150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +150.254.74.235 - - [26/Feb/2008:05:16:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +150.254.74.235 - - [26/Feb/2008:05:16:58 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.58.212.53 - - [26/Feb/2008:05:20:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.212.53 - - [26/Feb/2008:05:20:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.224.90 - - [26/Feb/2008:05:28:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:05:28:38 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.224.90 - - [26/Feb/2008:05:29:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.219.208.169 - - [26/Feb/2008:05:45:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.219.208.169 - - [26/Feb/2008:05:45:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.219.208.169 - - [26/Feb/2008:05:45:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.14 - - [26/Feb/2008:05:51:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +137.226.57.203 - - [26/Feb/2008:05:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:05:55:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:05:55:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.34.14 - - [26/Feb/2008:05:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +200.171.34.14 - - [26/Feb/2008:05:57:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.171.98.139 - - [26/Feb/2008:06:02:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +203.171.98.139 - - [26/Feb/2008:06:02:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:03:29 -0600] "GET /ply/ HTTP/1.0" 200 8018 +217.218.215.8 - - [26/Feb/2008:06:03:32 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +217.218.215.8 - - [26/Feb/2008:06:04:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.218.215.8 - - [26/Feb/2008:06:04:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +62.219.208.169 - - [26/Feb/2008:06:05:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.149 - - [26/Feb/2008:06:05:49 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.0" 200 731143 +203.171.98.139 - - [26/Feb/2008:06:05:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +203.171.98.139 - - [26/Feb/2008:06:06:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +203.171.98.139 - - [26/Feb/2008:06:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +62.219.208.169 - - [26/Feb/2008:06:08:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:06:11:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:11:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +193.213.227.243 - - [26/Feb/2008:06:11:25 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:06:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +193.213.227.243 - - [26/Feb/2008:06:11:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +122.55.236.192 - - [26/Feb/2008:06:14:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +131.111.47.93 - - [26/Feb/2008:06:16:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.41.139.41 - - [26/Feb/2008:06:24:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.41.139.41 - - [26/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.235.1.29 - - [26/Feb/2008:06:25:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.235.1.29 - - [26/Feb/2008:06:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.235.1.29 - - [26/Feb/2008:06:25:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.244.30.18 - - [26/Feb/2008:06:29:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:30:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.244.30.18 - - [26/Feb/2008:06:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +87.244.30.18 - - [26/Feb/2008:06:30:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.221.19.2 - - [26/Feb/2008:06:32:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.221.19.2 - - [26/Feb/2008:06:32:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 21275 +219.64.4.24 - - [26/Feb/2008:06:34:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +219.64.4.24 - - [26/Feb/2008:06:34:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqHPUXSharedLibraries HTTP/1.0" 200 3683 +62.153.70.82 - - [26/Feb/2008:06:40:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:06:40:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:06:40:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:06:40:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.213.227.243 - - [26/Feb/2008:06:42:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +193.213.227.243 - - [26/Feb/2008:06:42:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.0" 200 5648 +84.110.129.69 - - [26/Feb/2008:06:42:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.129.69 - - [26/Feb/2008:06:42:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 999 +203.73.43.189 - - [26/Feb/2008:06:46:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.24.58.194 - - [26/Feb/2008:06:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.58.194 - - [26/Feb/2008:06:48:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.24.58.194 - - [26/Feb/2008:06:48:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply HTTP/1.1" 301 242 +141.84.220.111 - - [26/Feb/2008:06:50:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.84.220.111 - - [26/Feb/2008:06:50:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.67.152.241 - - [26/Feb/2008:06:52:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.67.152.241 - - [26/Feb/2008:06:52:08 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [26/Feb/2008:06:54:43 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 243188 +71.57.91.136 - - [26/Feb/2008:06:54:44 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 +141.84.220.111 - - [26/Feb/2008:06:55:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.198.156.6 - - [26/Feb/2008:06:55:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +199.111.224.90 - - [26/Feb/2008:06:57:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:06:57:24 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.224.90 - - [26/Feb/2008:07:01:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:01:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.226.179.4 - - [26/Feb/2008:07:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +194.226.179.4 - - [26/Feb/2008:07:02:19 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +194.226.179.4 - - [26/Feb/2008:07:02:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.200.218.2 - - [26/Feb/2008:07:03:54 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +85.179.0.240 - - [26/Feb/2008:07:03:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.224.90 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.179.0.240 - - [26/Feb/2008:07:03:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.179.0.240 - - [26/Feb/2008:07:04:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.224.90 - - [26/Feb/2008:07:04:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.224.90 - - [26/Feb/2008:07:05:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.134 - - [26/Feb/2008:07:08:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1236 +137.226.57.203 - - [26/Feb/2008:07:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +137.226.57.203 - - [26/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:07:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.146.189.12 - - [26/Feb/2008:07:10:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +62.153.70.82 - - [26/Feb/2008:07:14:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.153.70.82 - - [26/Feb/2008:07:14:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +62.153.70.82 - - [26/Feb/2008:07:15:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.153.70.82 - - [26/Feb/2008:07:15:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:20:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.197.76.25 - - [26/Feb/2008:07:22:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.197.76.25 - - [26/Feb/2008:07:22:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:23:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.197.76.25 - - [26/Feb/2008:07:23:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +159.149.89.40 - - [26/Feb/2008:07:24:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +159.149.89.40 - - [26/Feb/2008:07:24:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +217.197.76.25 - - [26/Feb/2008:07:26:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.224.90 - - [26/Feb/2008:07:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.22.22 - - [26/Feb/2008:07:43:23 -0600] "GET /cartage/index.html HTTP/1.0" 404 133 +199.111.224.90 - - [26/Feb/2008:07:44:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:46:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.239.61.147 - - [26/Feb/2008:07:49:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.246.129.196 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [26/Feb/2008:07:52:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +81.80.245.157 - - [26/Feb/2008:07:53:56 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +81.80.245.157 - - [26/Feb/2008:07:54:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +66.232.113.194 - - [26/Feb/2008:07:55:43 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +200.51.41.29 - - [26/Feb/2008:07:55:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +161.53.125.15 - - [26/Feb/2008:07:55:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +218.109.113.209 - - [26/Feb/2008:07:57:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.109.113.209 - - [26/Feb/2008:07:57:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +64.22.160.1 - - [26/Feb/2008:07:57:55 -0600] "GET / HTTP/1.1" 200 4447 +64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +64.22.160.1 - - [26/Feb/2008:07:58:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +121.95.130.217 - - [26/Feb/2008:07:58:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +121.95.130.217 - - [26/Feb/2008:07:58:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.245.137.240 - - [26/Feb/2008:08:00:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.137.240 - - [26/Feb/2008:08:00:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.137.240 - - [26/Feb/2008:08:00:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.3 - - [26/Feb/2008:08:08:01 -0600] "GET / HTTP/1.1" 200 4447 +77.91.224.3 - - [26/Feb/2008:08:10:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.170.204.27 - - [26/Feb/2008:08:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.170.204.27 - - [26/Feb/2008:08:11:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.170.204.27 - - [26/Feb/2008:08:11:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +64.233.178.136 - - [26/Feb/2008:08:11:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.170.204.27 - - [26/Feb/2008:08:12:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +199.111.224.90 - - [26/Feb/2008:08:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.74.202.25 - - [26/Feb/2008:08:12:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.205.208.176 - - [26/Feb/2008:08:15:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +75.205.208.176 - - [26/Feb/2008:08:15:55 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +217.65.240.234 - - [26/Feb/2008:08:18:17 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.65.240.234 - - [26/Feb/2008:08:18:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.239.61.147 - - [26/Feb/2008:08:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.84.68.242 - - [26/Feb/2008:08:29:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +75.205.208.176 - - [26/Feb/2008:08:30:00 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 1373410 +87.84.68.242 - - [26/Feb/2008:08:30:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.221.197.20 - - [26/Feb/2008:08:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +59.103.3.200 - - [26/Feb/2008:08:33:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +206.51.237.114 - - [26/Feb/2008:08:33:01 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +59.103.3.200 - - [26/Feb/2008:08:33:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +196.207.40.212 - - [26/Feb/2008:08:33:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +143.248.134.61 - - [26/Feb/2008:08:33:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.179.223.243 - - [26/Feb/2008:08:33:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +89.179.223.243 - - [26/Feb/2008:08:34:02 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1296 +202.160.174.4 - - [26/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [26/Feb/2008:08:36:38 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.160.174.4 - - [26/Feb/2008:08:37:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +202.160.174.4 - - [26/Feb/2008:08:38:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.205.212.172 - - [26/Feb/2008:08:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.205.212.172 - - [26/Feb/2008:08:42:00 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +193.205.212.172 - - [26/Feb/2008:08:42:05 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/ApiFreeze HTTP/1.1" 200 4108 +193.205.212.172 - - [26/Feb/2008:08:42:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +193.205.212.172 - - [26/Feb/2008:08:42:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +193.205.212.172 - - [26/Feb/2008:08:42:26 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/ HTTP/1.1" 304 - +122.117.168.219 - - [26/Feb/2008:08:43:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +122.117.168.219 - - [26/Feb/2008:08:43:23 -0600] "GET /ply/example.html HTTP/1.1" 304 - +200.21.209.183 - - [26/Feb/2008:08:45:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:45:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:11 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.100.130.8 - - [26/Feb/2008:08:46:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [26/Feb/2008:08:46:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 +200.21.209.183 - - [26/Feb/2008:08:47:18 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.156.57.189 - - [26/Feb/2008:08:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.156.57.189 - - [26/Feb/2008:08:49:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +213.156.57.189 - - [26/Feb/2008:08:49:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +137.226.57.203 - - [26/Feb/2008:08:50:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:08:50:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:08:50:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.23.124.137 - - [26/Feb/2008:08:50:41 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 206 3623122 +88.112.138.119 - - [26/Feb/2008:08:53:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.112.138.119 - - [26/Feb/2008:08:53:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.112.138.119 - - [26/Feb/2008:08:53:32 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +130.236.182.96 - - [26/Feb/2008:08:55:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.35.216.121 - - [26/Feb/2008:09:03:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.35.216.121 - - [26/Feb/2008:09:03:17 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +82.35.216.121 - - [26/Feb/2008:09:03:19 -0600] "GET /cgi-bin/wiki.pl?action=rc&from=1201142973 HTTP/1.1" 200 1887 +82.35.216.121 - - [26/Feb/2008:09:03:21 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 +82.35.216.121 - - [26/Feb/2008:09:03:33 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=365 HTTP/1.1" 200 9413 +82.35.216.121 - - [26/Feb/2008:09:03:40 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +193.190.253.151 - - [26/Feb/2008:09:12:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.190.253.151 - - [26/Feb/2008:09:12:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +67.195.44.110 - - [26/Feb/2008:09:12:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +216.171.98.77 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.85.1.1 - - [26/Feb/2008:09:19:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.85.1.1 - - [26/Feb/2008:09:19:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +75.23.124.137 - - [26/Feb/2008:09:20:40 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:09:22:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:09:22:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +209.120.207.235 - - [26/Feb/2008:09:23:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.160.138.222 - - [26/Feb/2008:09:23:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.160.138.222 - - [26/Feb/2008:09:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +98.193.69.179 - - [26/Feb/2008:09:24:22 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +150.210.155.167 - - [26/Feb/2008:09:26:51 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +87.65.165.194 - - [26/Feb/2008:09:27:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.226.57.203 - - [26/Feb/2008:09:34:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [26/Feb/2008:09:36:05 -0600] "GET /robots.txt HTTP/1.0" 200 71 +202.160.179.46 - - [26/Feb/2008:09:36:13 -0600] "GET /ply/ HTTP/1.0" 304 - +138.100.217.162 - - [26/Feb/2008:09:39:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.217.162 - - [26/Feb/2008:09:39:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.160.172.70 - - [26/Feb/2008:09:39:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.160.172.70 - - [26/Feb/2008:09:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.217.162 - - [26/Feb/2008:09:39:49 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +192.54.144.229 - - [26/Feb/2008:09:41:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [26/Feb/2008:09:41:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [26/Feb/2008:09:42:18 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +208.22.104.18 - - [26/Feb/2008:09:42:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +148.87.1.172 - - [26/Feb/2008:09:43:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +148.87.1.172 - - [26/Feb/2008:09:44:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +201.236.226.90 - - [26/Feb/2008:09:44:34 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [26/Feb/2008:09:44:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:09:44:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:09:44:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +64.156.216.96 - - [26/Feb/2008:09:44:57 -0600] "GET /python.html HTTP/1.1" 200 18870 +64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +64.156.216.96 - - [26/Feb/2008:09:45:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +63.239.69.1 - - [26/Feb/2008:09:47:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +63.239.69.1 - - [26/Feb/2008:09:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +64.156.216.96 - - [26/Feb/2008:09:48:33 -0600] "GET /training.html HTTP/1.1" 200 6154 +204.246.129.196 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +128.29.43.1 - - [26/Feb/2008:09:48:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [26/Feb/2008:09:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.24.76 - - [26/Feb/2008:09:51:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +63.239.69.1 - - [26/Feb/2008:09:53:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +63.239.69.1 - - [26/Feb/2008:09:53:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +63.239.69.1 - - [26/Feb/2008:09:53:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +63.239.69.1 - - [26/Feb/2008:09:54:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +67.195.58.174 - - [26/Feb/2008:09:54:12 -0600] "GET /ply/ HTTP/1.0" 304 - +63.239.69.1 - - [26/Feb/2008:09:54:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleLanguageSharedLibraries HTTP/1.1" 200 2600 +216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.191.234.70 - - [26/Feb/2008:09:54:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.7.75 - - [26/Feb/2008:09:55:13 -0600] "GET /swig/SWIG_Doc1.pdf HTTP/1.0" 304 - +89.128.52.247 - - [26/Feb/2008:09:55:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.128.52.247 - - [26/Feb/2008:09:55:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.128.52.247 - - [26/Feb/2008:09:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [26/Feb/2008:10:04:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.117 - - [26/Feb/2008:10:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [26/Feb/2008:10:09:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 200 990 +192.54.144.229 - - [26/Feb/2008:10:10:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +68.145.52.154 - - [26/Feb/2008:10:12:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /python.html HTTP/1.1" 200 18870 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +18.188.70.114 - - [26/Feb/2008:10:15:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +18.188.70.114 - - [26/Feb/2008:10:15:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +139.149.31.231 - - [26/Feb/2008:10:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.193.220.201 - - [26/Feb/2008:10:20:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +63.239.69.1 - - [26/Feb/2008:10:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.1" 200 2052 +68.145.52.154 - - [26/Feb/2008:10:24:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.145.52.154 - - [26/Feb/2008:10:24:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.187.18 - - [26/Feb/2008:10:25:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.187.18 - - [26/Feb/2008:10:26:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.121.187.18 - - [26/Feb/2008:10:26:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +80.121.187.18 - - [26/Feb/2008:10:26:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.187.18 - - [26/Feb/2008:10:27:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [26/Feb/2008:10:30:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.175.40.146 - - [26/Feb/2008:10:30:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.172.19.20 - - [26/Feb/2008:10:30:51 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +64.175.40.146 - - [26/Feb/2008:10:31:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.169.46.98 - - [26/Feb/2008:10:31:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.41.243.144 - - [26/Feb/2008:10:31:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +122.117.168.219 - - [26/Feb/2008:10:32:12 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [26/Feb/2008:10:33:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.191.234.70 - - [26/Feb/2008:10:37:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +216.191.234.70 - - [26/Feb/2008:10:38:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +216.191.234.70 - - [26/Feb/2008:10:39:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +38.104.0.30 - - [26/Feb/2008:10:43:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.104.0.30 - - [26/Feb/2008:10:43:24 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.223.13.234 - - [26/Feb/2008:10:44:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +76.223.13.234 - - [26/Feb/2008:10:44:09 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +18.188.70.114 - - [26/Feb/2008:10:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.230.107 - - [26/Feb/2008:10:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.230.107 - - [26/Feb/2008:10:47:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:10:51:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [26/Feb/2008:10:51:47 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.249.251.158 - - [26/Feb/2008:10:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [26/Feb/2008:10:53:46 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +141.35.1.57 - - [26/Feb/2008:10:54:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +141.35.1.57 - - [26/Feb/2008:10:54:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +141.35.1.57 - - [26/Feb/2008:10:54:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +141.35.1.57 - - [26/Feb/2008:10:55:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +141.35.1.57 - - [26/Feb/2008:10:56:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.0" 200 2725 +128.143.230.107 - - [26/Feb/2008:10:57:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +189.177.109.221 - - [26/Feb/2008:11:01:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.177.109.221 - - [26/Feb/2008:11:01:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +217.196.43.134 - - [26/Feb/2008:11:05:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.127.17.194 - - [26/Feb/2008:11:06:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.127.17.194 - - [26/Feb/2008:11:06:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.127.17.194 - - [26/Feb/2008:11:06:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.1" 200 69278 +205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 +205.196.222.10 - - [26/Feb/2008:11:07:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [26/Feb/2008:11:09:44 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +194.117.40.162 - - [26/Feb/2008:11:10:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:10:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.117.40.162 - - [26/Feb/2008:11:11:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +194.117.40.162 - - [26/Feb/2008:11:11:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +24.7.210.64 - - [26/Feb/2008:11:12:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [26/Feb/2008:11:13:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +149.65.130.31 - - [26/Feb/2008:11:13:23 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +149.65.130.31 - - [26/Feb/2008:11:13:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 997 +24.1.159.241 - - [26/Feb/2008:11:16:40 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:16:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:17:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [26/Feb/2008:11:17:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.67.6.11 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply HTTP/1.1" 301 242 +204.246.129.196 - - [26/Feb/2008:11:20:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [26/Feb/2008:11:23:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:11:23:28 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:11:23:31 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [26/Feb/2008:11:23:32 -0600] "GET / HTTP/1.1" 200 4447 +84.233.245.68 - - [26/Feb/2008:11:24:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +84.233.245.68 - - [26/Feb/2008:11:24:42 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +84.233.245.68 - - [26/Feb/2008:11:25:19 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1775 +84.233.245.68 - - [26/Feb/2008:11:25:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +84.233.245.68 - - [26/Feb/2008:11:25:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.0" 200 11548 +38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +38.113.160.194 - - [26/Feb/2008:11:30:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.113.160.194 - - [26/Feb/2008:11:30:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +38.113.160.194 - - [26/Feb/2008:11:30:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.125.239 - - [26/Feb/2008:11:31:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:31:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:31:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.125.239 - - [26/Feb/2008:11:31:28 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +38.113.160.194 - - [26/Feb/2008:11:32:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +38.113.160.194 - - [26/Feb/2008:11:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +129.42.208.182 - - [26/Feb/2008:11:39:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.42.208.182 - - [26/Feb/2008:11:39:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +72.172.42.58 - - [26/Feb/2008:11:42:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.0.246.111 - - [26/Feb/2008:11:47:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +90.0.246.111 - - [26/Feb/2008:11:47:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.0.246.111 - - [26/Feb/2008:11:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [26/Feb/2008:11:51:14 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +74.6.25.20 - - [26/Feb/2008:11:57:01 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.23.225 - - [26/Feb/2008:11:57:01 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +72.22.5.152 - - [26/Feb/2008:11:57:40 -0600] "GET /cv.html HTTP/1.1" 200 31798 +195.212.29.92 - - [26/Feb/2008:11:59:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:22 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:00:37 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +24.15.187.198 - - [26/Feb/2008:12:00:44 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +129.42.208.182 - - [26/Feb/2008:12:02:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET / HTTP/1.1" 200 4447 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.125.245 - - [26/Feb/2008:12:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +128.135.125.245 - - [26/Feb/2008:12:07:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.213.67.31 - - [26/Feb/2008:12:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.204.246 - - [26/Feb/2008:12:14:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.204.246 - - [26/Feb/2008:12:14:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 +195.131.84.202 - - [26/Feb/2008:12:17:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +195.131.84.219 - - [26/Feb/2008:12:17:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.3.45.46 - - [26/Feb/2008:12:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.3.45.46 - - [26/Feb/2008:12:18:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.131.84.251 - - [26/Feb/2008:12:19:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.152.228 - - [26/Feb/2008:12:21:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.152.228 - - [26/Feb/2008:12:23:05 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 +128.135.152.228 - - [26/Feb/2008:12:23:36 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 +128.135.152.228 - - [26/Feb/2008:12:24:44 -0600] "GET /dynamic/07Functional.pdf HTTP/1.0" 200 133908 +128.135.152.228 - - [26/Feb/2008:12:25:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.202.49.172 - - [26/Feb/2008:12:33:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [26/Feb/2008:12:34:04 -0600] "GET / HTTP/1.1" 200 4447 +67.195.44.110 - - [26/Feb/2008:12:35:28 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [26/Feb/2008:12:35:46 -0600] "GET /ply/ HTTP/1.0" 200 8018 +38.113.160.194 - - [26/Feb/2008:12:38:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +38.113.160.194 - - [26/Feb/2008:12:38:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +38.113.160.194 - - [26/Feb/2008:12:38:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +24.15.187.198 - - [26/Feb/2008:12:38:54 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +24.15.187.198 - - [26/Feb/2008:12:39:17 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:39:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +72.30.226.134 - - [26/Feb/2008:12:40:09 -0600] "GET /ply/ HTTP/1.0" 200 8018 +64.3.45.46 - - [26/Feb/2008:12:40:43 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +24.15.187.198 - - [26/Feb/2008:12:40:48 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +204.154.183.65 - - [26/Feb/2008:12:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [26/Feb/2008:12:40:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.154.183.65 - - [26/Feb/2008:12:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [26/Feb/2008:12:43:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +216.148.212.222 - - [26/Feb/2008:12:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.148.212.222 - - [26/Feb/2008:12:44:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.157.220.80 - - [26/Feb/2008:12:45:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.157.220.80 - - [26/Feb/2008:12:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.3.45.46 - - [26/Feb/2008:12:47:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +64.3.45.46 - - [26/Feb/2008:12:47:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [26/Feb/2008:12:48:37 -0600] "GET /photos/u505/pages/IMG_1514.htm HTTP/1.0" 404 133 +65.54.99.91 - - [26/Feb/2008:12:49:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +65.54.99.91 - - [26/Feb/2008:12:49:09 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 200 64334 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 200 107720 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +65.54.99.91 - - [26/Feb/2008:12:49:10 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.1" 200 66002 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET / HTTP/1.1" 200 4447 +65.54.99.91 - - [26/Feb/2008:12:49:11 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 +65.54.99.91 - - [26/Feb/2008:12:49:12 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.1" 200 74610 +24.15.187.198 - - [26/Feb/2008:12:51:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:52:00 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +24.15.187.198 - - [26/Feb/2008:12:52:13 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +195.252.125.134 - - [26/Feb/2008:12:52:13 -0600] "GET /swig/swig-1.3.28rc2.tar.gz HTTP/1.1" 200 3972567 +24.15.187.198 - - [26/Feb/2008:12:52:30 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +24.15.187.198 - - [26/Feb/2008:12:52:39 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [26/Feb/2008:12:56:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:56:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [26/Feb/2008:12:56:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +13.13.16.1 - - [26/Feb/2008:12:58:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - +129.42.208.182 - - [26/Feb/2008:12:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +129.42.208.182 - - [26/Feb/2008:12:59:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.27.16 - - [26/Feb/2008:13:11:51 -0600] "GET /software.html HTTP/1.0" 304 - +148.241.78.63 - - [26/Feb/2008:13:12:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.78.63 - - [26/Feb/2008:13:12:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.78.63 - - [26/Feb/2008:13:12:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.109.126 - - [26/Feb/2008:13:13:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.109.126 - - [26/Feb/2008:13:13:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:13:13:52 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +161.45.109.126 - - [26/Feb/2008:13:14:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:14:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.105.199 - - [26/Feb/2008:13:14:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.109.126 - - [26/Feb/2008:13:14:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:15:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.105.199 - - [26/Feb/2008:13:18:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.218.181 - - [26/Feb/2008:13:23:08 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.218.181 - - [26/Feb/2008:13:23:17 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 969 +89.165.73.169 - - [26/Feb/2008:13:32:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.32.37.138 - - [26/Feb/2008:13:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.191.234.70 - - [26/Feb/2008:13:33:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +216.191.234.70 - - [26/Feb/2008:13:33:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqIrixSharedLibraries HTTP/1.1" 200 2105 +88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.248.226.142 - - [26/Feb/2008:13:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/index.html HTTP/1.1" 200 40215 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/hello.gif HTTP/1.1" 200 1118 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame0.gif HTTP/1.1" 200 432 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/gp.gif HTTP/1.1" 200 43556 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame1.gif HTTP/1.1" 200 2588 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/frame2.gif HTTP/1.1" 200 2237 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/clip.gif HTTP/1.1" 200 21566 +64.113.185.61 - - [26/Feb/2008:13:40:12 -0600] "GET /gifplot/plot2d.gif HTTP/1.1" 200 5902 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d.gif HTTP/1.1" 200 2212 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_2.gif HTTP/1.1" 200 888 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_3.gif HTTP/1.1" 200 1194 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_4.gif HTTP/1.1" 200 811 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_5.gif HTTP/1.1" 200 1223 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/view3d_6.gif HTTP/1.1" 200 1870 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_1.gif HTTP/1.1" 200 16420 +64.113.185.61 - - [26/Feb/2008:13:40:13 -0600] "GET /gifplot/plot3d_2.gif HTTP/1.1" 200 34142 +64.113.185.61 - - [26/Feb/2008:13:40:14 -0600] "GET /gifplot/plot3d_3.gif HTTP/1.1" 200 55793 +128.221.197.20 - - [26/Feb/2008:13:42:31 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +148.241.78.63 - - [26/Feb/2008:13:47:47 -0600] "GET /ply/ HTTP/1.1" 304 - +148.241.78.63 - - [26/Feb/2008:13:47:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +88.90.231.185 - - [26/Feb/2008:13:48:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.125.245 - - [26/Feb/2008:13:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.90.231.185 - - [26/Feb/2008:13:52:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.245 - - [26/Feb/2008:13:52:11 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +216.54.141.250 - - [26/Feb/2008:13:52:56 -0600] "GET /python.html HTTP/1.1" 200 18870 +216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +216.54.141.250 - - [26/Feb/2008:13:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.16.64.124 - - [26/Feb/2008:13:55:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.16.64.124 - - [26/Feb/2008:13:55:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.16.64.124 - - [26/Feb/2008:13:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.16.64.124 - - [26/Feb/2008:13:55:54 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.109.126 - - [26/Feb/2008:13:59:38 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.109.126 - - [26/Feb/2008:14:06:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 12705 +89.165.73.169 - - [26/Feb/2008:14:08:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:14:19:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +148.241.78.63 - - [26/Feb/2008:14:19:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +148.241.78.63 - - [26/Feb/2008:14:19:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.151 - - [26/Feb/2008:14:21:41 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +88.111.158.229 - - [26/Feb/2008:14:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +88.111.158.229 - - [26/Feb/2008:14:23:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [26/Feb/2008:14:25:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +90.185.65.84 - - [26/Feb/2008:14:25:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [26/Feb/2008:14:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.22.104.18 - - [26/Feb/2008:14:30:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +208.22.104.18 - - [26/Feb/2008:14:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +128.221.197.20 - - [26/Feb/2008:14:30:42 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +208.22.104.18 - - [26/Feb/2008:14:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMultipleInputTypemaps HTTP/1.1" 200 2300 +90.185.65.84 - - [26/Feb/2008:14:31:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +208.22.104.18 - - [26/Feb/2008:14:31:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +208.22.104.18 - - [26/Feb/2008:14:32:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +208.22.104.18 - - [26/Feb/2008:14:34:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +208.22.104.18 - - [26/Feb/2008:14:34:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +208.22.104.18 - - [26/Feb/2008:14:35:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +67.195.58.172 - - [26/Feb/2008:14:42:14 -0600] "GET /index.html HTTP/1.0" 304 - +80.91.229.6 - - [26/Feb/2008:14:44:58 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /robots.txt HTTP/1.1" 200 71 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.91.229.6 - - [26/Feb/2008:14:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 +80.91.229.6 - - [26/Feb/2008:14:45:00 -0600] "GET / HTTP/1.1" 200 4447 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.21.160.34 - - [26/Feb/2008:14:47:47 -0600] "GET / HTTP/1.1" 200 4447 +129.21.160.34 - - [26/Feb/2008:14:47:48 -0600] "GET / HTTP/1.1" 200 4447 +128.221.197.20 - - [26/Feb/2008:14:50:57 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.164.42.125 - - [26/Feb/2008:14:53:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.164.42.125 - - [26/Feb/2008:14:53:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.164.42.125 - - [26/Feb/2008:14:53:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +77.91.224.3 - - [26/Feb/2008:14:54:07 -0600] "GET /software.html HTTP/1.1" 200 3163 +74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [26/Feb/2008:14:54:22 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.164.42.125 - - [26/Feb/2008:14:54:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.13 - - [26/Feb/2008:14:54:32 -0600] "GET /training.html HTTP/1.1" 200 6154 +68.164.42.125 - - [26/Feb/2008:14:54:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic HTTP/1.1" 301 246 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.254 - - [26/Feb/2008:14:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.120.207.254 - - [26/Feb/2008:14:55:35 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +200.21.98.7 - - [26/Feb/2008:14:55:48 -0600] "GET / HTTP/1.0" 200 4447 +200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.21.98.7 - - [26/Feb/2008:14:55:52 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +209.120.207.254 - - [26/Feb/2008:14:55:58 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +84.110.202.119 - - [26/Feb/2008:14:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.202.119 - - [26/Feb/2008:14:56:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +77.91.224.3 - - [26/Feb/2008:14:56:17 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +171.161.224.10 - - [26/Feb/2008:14:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +171.161.224.10 - - [26/Feb/2008:14:56:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:14:57:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +77.91.224.13 - - [26/Feb/2008:14:57:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 +77.91.224.3 - - [26/Feb/2008:14:59:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +77.91.224.13 - - [26/Feb/2008:15:00:09 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.235 - - [26/Feb/2008:15:01:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +200.21.98.7 - - [26/Feb/2008:15:02:31 -0600] "GET /images/Davetubes.jpg HTTP/1.0" 200 31530 +139.140.198.50 - - [26/Feb/2008:15:06:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 189268 +82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.135.12.203 - - [26/Feb/2008:15:14:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:14:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +82.135.12.203 - - [26/Feb/2008:15:14:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +82.135.12.203 - - [26/Feb/2008:15:14:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +86.157.119.197 - - [26/Feb/2008:15:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:19:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.135.12.203 - - [26/Feb/2008:15:19:25 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.135.12.203 - - [26/Feb/2008:15:19:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +24.10.16.193 - - [26/Feb/2008:15:19:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.135.12.203 - - [26/Feb/2008:15:19:38 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [26/Feb/2008:15:19:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.135.12.203 - - [26/Feb/2008:15:20:21 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4233 +24.10.16.193 - - [26/Feb/2008:15:23:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.10.16.193 - - [26/Feb/2008:15:23:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +88.112.138.119 - - [26/Feb/2008:15:23:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET / HTTP/1.1" 200 4447 +192.55.2.36 - - [26/Feb/2008:15:23:59 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /about.html HTTP/1.1" 200 7890 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +192.55.2.36 - - [26/Feb/2008:15:24:17 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +192.55.2.36 - - [26/Feb/2008:15:24:46 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +192.55.2.36 - - [26/Feb/2008:15:24:51 -0600] "GET /diet.html HTTP/1.1" 404 133 +192.55.2.36 - - [26/Feb/2008:15:25:02 -0600] "GET /index.html HTTP/1.1" 200 4447 +88.191.19.81 - - [26/Feb/2008:15:31:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic HTTP/1.1" 301 246 +209.120.207.235 - - [26/Feb/2008:15:33:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +209.120.207.235 - - [26/Feb/2008:15:33:35 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +139.11.6.202 - - [26/Feb/2008:15:37:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +139.11.6.202 - - [26/Feb/2008:15:37:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +139.11.6.202 - - [26/Feb/2008:15:38:13 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +139.11.6.202 - - [26/Feb/2008:15:38:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +139.11.6.202 - - [26/Feb/2008:15:38:27 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 +139.11.6.202 - - [26/Feb/2008:15:38:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +139.11.6.202 - - [26/Feb/2008:15:38:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.0" 200 1927 +139.11.6.202 - - [26/Feb/2008:15:38:45 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.0" 200 4493 +139.11.6.202 - - [26/Feb/2008:15:38:51 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.0" 200 1579 +139.11.6.202 - - [26/Feb/2008:15:38:55 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.0" 200 8981 +66.249.65.37 - - [26/Feb/2008:15:39:16 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [26/Feb/2008:15:39:17 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 304 - +82.41.78.129 - - [26/Feb/2008:15:43:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.41.78.129 - - [26/Feb/2008:15:43:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.41.78.129 - - [26/Feb/2008:15:43:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +88.112.138.119 - - [26/Feb/2008:15:44:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +79.69.70.197 - - [26/Feb/2008:15:46:01 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +79.69.70.197 - - [26/Feb/2008:15:46:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.41.78.129 - - [26/Feb/2008:15:48:33 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.19.115 - - [26/Feb/2008:15:49:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [26/Feb/2008:15:49:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +146.189.58.99 - - [26/Feb/2008:15:53:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +146.189.58.99 - - [26/Feb/2008:15:53:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +146.189.58.99 - - [26/Feb/2008:15:53:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +146.189.58.99 - - [26/Feb/2008:15:53:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +85.228.229.73 - - [26/Feb/2008:15:55:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.228.229.73 - - [26/Feb/2008:15:55:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.228.229.73 - - [26/Feb/2008:15:55:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +85.228.229.73 - - [26/Feb/2008:15:56:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +85.228.229.73 - - [26/Feb/2008:15:56:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAutotoolsConfiguration HTTP/1.1" 200 5005 +38.98.120.84 - - [26/Feb/2008:15:56:14 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [26/Feb/2008:15:56:15 -0600] "GET / HTTP/1.1" 200 4447 +216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.9.243.111 - - [26/Feb/2008:15:57:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.9.243.111 - - [26/Feb/2008:15:57:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [26/Feb/2008:15:58:32 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +216.9.243.111 - - [26/Feb/2008:15:58:53 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 304 - +128.135.125.239 - - [26/Feb/2008:15:59:12 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/ HTTP/1.1" 304 - +216.9.243.111 - - [26/Feb/2008:15:59:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +208.80.193.45 - - [26/Feb/2008:16:00:46 -0600] "GET / HTTP/1.1" 200 4447 +89.128.216.67 - - [26/Feb/2008:16:01:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.128.216.67 - - [26/Feb/2008:16:01:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.128.216.67 - - [26/Feb/2008:16:01:24 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +128.8.114.84 - - [26/Feb/2008:16:03:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +128.8.114.84 - - [26/Feb/2008:16:03:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.8.114.84 - - [26/Feb/2008:16:03:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +208.97.218.10 - - [26/Feb/2008:16:06:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [26/Feb/2008:16:06:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +208.97.218.10 - - [26/Feb/2008:16:06:41 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.249.65.37 - - [26/Feb/2008:16:09:51 -0600] "GET /software.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:11 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:15 -0600] "GET /ply/README HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:10:34 -0600] "GET /ply/?ref=%C4%B0lkSexShop.Com HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:11:18 -0600] "GET /writing.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:11:29 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:06 -0600] "GET /ply/ HTTP/1.1" 304 - +128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.195.84 - - [26/Feb/2008:16:12:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.100.195.84 - - [26/Feb/2008:16:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/assign1.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:14 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:30 -0600] "GET /consulting.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:12:49 -0600] "GET /ply HTTP/1.1" 301 242 +66.249.65.37 - - [26/Feb/2008:16:12:58 -0600] "GET /ply/index.html HTTP/1.1" 304 - +66.249.65.37 - - [26/Feb/2008:16:13:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.195.84 - - [26/Feb/2008:16:14:03 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 212696 +67.173.185.186 - - [26/Feb/2008:16:14:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213580 +67.173.185.186 - - [26/Feb/2008:16:14:16 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 213787 +67.173.185.186 - - [26/Feb/2008:16:16:05 -0600] "GET / HTTP/1.1" 200 4447 +67.173.185.186 - - [26/Feb/2008:16:16:06 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.173.185.186 - - [26/Feb/2008:16:16:42 -0600] "GET /assign5.html HTTP/1.1" 404 133 +74.6.23.209 - - [26/Feb/2008:16:16:46 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1231 +67.173.185.186 - - [26/Feb/2008:16:16:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +68.12.248.157 - - [26/Feb/2008:16:20:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.16 - - [26/Feb/2008:16:24:32 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [26/Feb/2008:16:24:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 304 - +128.100.195.84 - - [26/Feb/2008:16:25:14 -0600] "GET /ply/README HTTP/1.1" 200 8605 +68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +68.1.82.124 - - [26/Feb/2008:16:30:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +99.167.100.246 - - [26/Feb/2008:16:33:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.167.100.246 - - [26/Feb/2008:16:33:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.21.188.140 - - [26/Feb/2008:16:33:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:16:33:59 -0600] "GET /ply/ HTTP/1.0" 200 8018 +190.24.33.53 - - [26/Feb/2008:16:34:13 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.233.178.136 - - [26/Feb/2008:16:35:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 +89.170.38.243 - - [26/Feb/2008:16:36:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.170.38.243 - - [26/Feb/2008:16:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:16:39:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.133.9.141 - - [26/Feb/2008:16:47:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.133.9.141 - - [26/Feb/2008:16:47:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +199.111.224.90 - - [26/Feb/2008:16:49:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [26/Feb/2008:16:52:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.133.9.141 - - [26/Feb/2008:16:53:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.133.9.141 - - [26/Feb/2008:16:53:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +199.111.224.90 - - [26/Feb/2008:16:54:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +199.198.216.189 - - [26/Feb/2008:16:55:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.198.216.189 - - [26/Feb/2008:16:55:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +199.198.216.189 - - [26/Feb/2008:16:56:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +210.143.35.17 - - [26/Feb/2008:16:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:17:05:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [26/Feb/2008:17:05:42 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.167.244.66 - - [26/Feb/2008:17:08:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/ HTTP/1.1" 304 - +202.226.91.246 - - [26/Feb/2008:17:15:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +202.226.91.246 - - [26/Feb/2008:17:17:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.100.109.143 - - [26/Feb/2008:17:27:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [26/Feb/2008:17:30:12 -0600] "GET /ply HTTP/1.1" 301 242 +67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET / HTTP/1.1" 200 4447 +67.228.115.170 - - [26/Feb/2008:17:32:30 -0600] "GET /index.html HTTP/1.1" 200 4447 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /software.html HTTP/1.1" 200 3163 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /about.html HTTP/1.1" 200 7890 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /python.html HTTP/1.1" 200 18870 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 +67.228.115.170 - - [26/Feb/2008:17:32:31 -0600] "GET /cv.html HTTP/1.1" 200 31798 +67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:32 -0600] "GET /syllabus.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:38 -0600] "GET /01Introduction.pdf HTTP/1.1" 200 2172146 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign1.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /soln1.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /02WorkingWithData.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /assign2.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:39 -0600] "GET /smackdown.py HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /03ProgramStructure.pdf HTTP/1.1" 200 279926 +67.228.115.170 - - [26/Feb/2008:17:32:40 -0600] "GET /assign3.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:42 -0600] "GET /04Objects.pdf HTTP/1.1" 200 502854 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /05ObjectModel.pdf HTTP/1.1" 200 719628 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign4.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /06FilesAndText.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /assign5.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /07Functional.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /ply.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /example.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /README HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /PLYTalk.pdf HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:45 -0600] "GET /support.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /exec.html HTTP/1.1" 404 133 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Usenix2001/beazley.pdf HTTP/1.1" 200 76713 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +67.228.115.170 - - [26/Feb/2008:17:32:46 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +67.228.115.170 - - [26/Feb/2008:17:32:47 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949 +67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.1" 200 82126 +67.228.115.170 - - [26/Feb/2008:17:32:48 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +67.228.115.170 - - [26/Feb/2008:17:32:49 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +67.228.115.170 - - [26/Feb/2008:17:32:50 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 +67.228.115.170 - - [26/Feb/2008:17:32:51 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +67.228.115.170 - - [26/Feb/2008:17:32:52 -0600] "GET /swigperl.pdf HTTP/1.1" 404 133 +128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.239.253 - - [26/Feb/2008:17:33:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.239.253 - - [26/Feb/2008:17:33:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [26/Feb/2008:17:34:47 -0600] "GET /ply/ HTTP/1.0" 304 - +203.144.160.249 - - [26/Feb/2008:17:38:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +67.195.58.182 - - [26/Feb/2008:17:38:50 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 304 - +67.195.58.186 - - [26/Feb/2008:17:38:53 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 304 - +128.143.136.157 - - [26/Feb/2008:17:39:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.144.160.249 - - [26/Feb/2008:17:39:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +203.144.160.249 - - [26/Feb/2008:17:39:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +67.195.58.151 - - [26/Feb/2008:17:39:32 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 304 - +67.195.58.170 - - [26/Feb/2008:17:39:38 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 304 - +203.144.160.249 - - [26/Feb/2008:17:39:46 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +67.195.58.181 - - [26/Feb/2008:17:40:14 -0600] "GET /ply/support.html HTTP/1.0" 304 - +67.195.58.170 - - [26/Feb/2008:17:40:17 -0600] "GET / HTTP/1.0" 304 - +67.195.58.177 - - [26/Feb/2008:17:41:00 -0600] "GET /python.html HTTP/1.0" 200 18870 +67.195.58.185 - - [26/Feb/2008:17:41:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +128.143.136.157 - - [26/Feb/2008:17:41:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:17:41:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.168 - - [26/Feb/2008:17:42:31 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.97.51.195 - - [26/Feb/2008:17:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [26/Feb/2008:17:45:39 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.0" 404 133 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.114.59.172 - - [26/Feb/2008:17:46:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.114.59.172 - - [26/Feb/2008:17:46:55 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +199.46.245.233 - - [26/Feb/2008:17:51:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +199.46.245.233 - - [26/Feb/2008:17:51:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.114.59.172 - - [26/Feb/2008:17:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [26/Feb/2008:17:54:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +69.213.243.113 - - [26/Feb/2008:17:54:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.195.58.164 - - [26/Feb/2008:17:55:03 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 304 - +69.213.243.113 - - [26/Feb/2008:17:57:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +69.213.243.113 - - [26/Feb/2008:17:57:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +69.213.243.113 - - [26/Feb/2008:17:58:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +74.6.22.11 - - [26/Feb/2008:18:02:12 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 +74.6.28.205 - - [26/Feb/2008:18:03:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /python.html HTTP/1.1" 200 18870 +216.31.211.11 - - [26/Feb/2008:18:03:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +216.31.211.11 - - [26/Feb/2008:18:03:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:18:03:36 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +217.196.43.134 - - [26/Feb/2008:18:05:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.22.217 - - [26/Feb/2008:18:17:27 -0600] "GET /dynamic/04Objects.pdf HTTP/1.0" 200 514533 +67.195.58.168 - - [26/Feb/2008:18:19:04 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 304 - +74.6.20.14 - - [26/Feb/2008:18:19:37 -0600] "GET /tenure/trip3.html HTTP/1.0" 404 133 +87.194.101.28 - - [26/Feb/2008:18:28:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.194.101.28 - - [26/Feb/2008:18:29:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12329 +87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.194.101.28 - - [26/Feb/2008:18:29:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.101.28 - - [26/Feb/2008:18:29:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.123 - - [26/Feb/2008:18:31:54 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE047.HTM HTTP/1.1" 304 - +128.143.136.157 - - [26/Feb/2008:18:35:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:35:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.86.204.99 - - [26/Feb/2008:18:39:33 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +38.98.120.84 - - [26/Feb/2008:18:39:55 -0600] "GET / HTTP/1.1" 200 4447 +65.57.245.11 - - [26/Feb/2008:18:40:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +66.232.113.194 - - [26/Feb/2008:18:43:36 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +130.86.204.99 - - [26/Feb/2008:18:43:43 -0600] "GET /ply/README HTTP/1.1" 200 8605 +125.244.152.66 - - [26/Feb/2008:18:43:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +202.216.177.14 - - [26/Feb/2008:18:43:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +85.185.11.131 - - [26/Feb/2008:18:43:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +216.148.212.222 - - [26/Feb/2008:18:50:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:18:55:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.145 - - [26/Feb/2008:18:56:31 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +206.51.237.114 - - [26/Feb/2008:19:02:12 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +213.29.144.10 - - [26/Feb/2008:19:02:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +132.181.247.105 - - [26/Feb/2008:19:07:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [26/Feb/2008:19:13:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE067.HTM HTTP/1.1" 304 - +65.55.208.123 - - [26/Feb/2008:19:13:59 -0600] "GET /photos/u505/pages/IMG_1517.htm HTTP/1.1" 404 133 +67.195.34.108 - - [26/Feb/2008:19:18:09 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +67.195.34.114 - - [26/Feb/2008:19:19:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:21:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:21:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:21:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:22:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:23:16 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.111 - - [26/Feb/2008:19:24:20 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.23.73 - - [26/Feb/2008:19:24:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 +62.59.179.107 - - [26/Feb/2008:19:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.79.38.12 - - [26/Feb/2008:19:27:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.79.38.12 - - [26/Feb/2008:19:27:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.79.38.12 - - [26/Feb/2008:19:28:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:29:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [26/Feb/2008:19:30:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.58.86.1 - - [26/Feb/2008:19:30:50 -0600] "GET /dynamic/ HTTP/1.1" 304 - +74.6.26.171 - - [26/Feb/2008:19:30:51 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [26/Feb/2008:19:30:51 -0600] "GET /ply/ HTTP/1.0" 200 8018 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.167.150 - - [26/Feb/2008:19:31:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.167.150 - - [26/Feb/2008:19:32:11 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +62.59.179.107 - - [26/Feb/2008:19:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.10 - - [26/Feb/2008:19:36:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:19:36:30 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +201.83.27.70 - - [26/Feb/2008:19:38:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [26/Feb/2008:19:39:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.83.27.70 - - [26/Feb/2008:19:39:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [26/Feb/2008:19:39:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.83.27.70 - - [26/Feb/2008:19:39:07 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +201.83.27.70 - - [26/Feb/2008:19:39:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.205.10 - - [26/Feb/2008:19:45:16 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.202.49.172 - - [26/Feb/2008:19:45:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 +128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [26/Feb/2008:19:56:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:56:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:58:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:19:58:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.91.57 - - [26/Feb/2008:20:00:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.91.57 - - [26/Feb/2008:20:00:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.6.19.236 - - [26/Feb/2008:20:04:05 -0600] "GET /training.html HTTP/1.0" 200 6154 +62.59.179.107 - - [26/Feb/2008:20:05:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.160.180.70 - - [26/Feb/2008:20:06:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +202.160.180.213 - - [26/Feb/2008:20:07:22 -0600] "GET / HTTP/1.0" 200 4447 +76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +76.189.146.60 - - [26/Feb/2008:20:10:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.189.146.60 - - [26/Feb/2008:20:10:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMaxOSXSharedLibraries HTTP/1.1" 200 9822 +76.189.146.60 - - [26/Feb/2008:20:11:10 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +76.189.146.60 - - [26/Feb/2008:20:11:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +76.189.146.60 - - [26/Feb/2008:20:11:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:20:11:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:20:12:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +192.94.38.34 - - [26/Feb/2008:20:16:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.130.247.245 - - [26/Feb/2008:20:27:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.130.247.245 - - [26/Feb/2008:20:27:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.249.147.81 - - [26/Feb/2008:20:28:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +60.249.147.81 - - [26/Feb/2008:20:28:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +60.249.147.81 - - [26/Feb/2008:20:28:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.94.38.34 - - [26/Feb/2008:20:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +192.94.38.34 - - [26/Feb/2008:20:29:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1631 +192.94.38.34 - - [26/Feb/2008:20:29:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +65.214.45.129 - - [26/Feb/2008:20:35:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply HTTP/1.1" 301 242 +205.214.235.18 - - [26/Feb/2008:20:40:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:40:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.214.235.18 - - [26/Feb/2008:20:41:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.249.52.137 - - [26/Feb/2008:20:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.249.52.137 - - [26/Feb/2008:20:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.46.29.140 - - [26/Feb/2008:20:45:10 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2739 +219.87.152.215 - - [26/Feb/2008:20:45:12 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +69.217.73.52 - - [26/Feb/2008:20:45:13 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +80.97.94.178 - - [26/Feb/2008:20:45:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.63.117.142 - - [26/Feb/2008:20:45:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +99.232.163.8 - - [26/Feb/2008:20:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [26/Feb/2008:20:45:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.195.58.174 - - [26/Feb/2008:20:46:21 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 304 - +67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/example.html HTTP/1.0" 304 - +67.195.58.188 - - [26/Feb/2008:20:47:24 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 304 - +67.195.58.178 - - [26/Feb/2008:20:47:49 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 304 - +67.176.147.11 - - [26/Feb/2008:20:51:20 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.176.147.11 - - [26/Feb/2008:20:51:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [26/Feb/2008:20:52:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [26/Feb/2008:20:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +99.232.163.8 - - [26/Feb/2008:20:53:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:20:53:48 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.249.52.137 - - [26/Feb/2008:20:54:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.249.52.137 - - [26/Feb/2008:20:54:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.249.52.137 - - [26/Feb/2008:20:54:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.22.21.146 - - [26/Feb/2008:20:58:16 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +203.73.43.189 - - [26/Feb/2008:20:59:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.189.93.50 - - [26/Feb/2008:21:00:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.189.93.50 - - [26/Feb/2008:21:01:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.189.93.50 - - [26/Feb/2008:21:01:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.110.229.30 - - [26/Feb/2008:21:04:00 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.229.30 - - [26/Feb/2008:21:04:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +82.249.138.163 - - [26/Feb/2008:21:07:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.249.138.163 - - [26/Feb/2008:21:07:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.249.138.163 - - [26/Feb/2008:21:07:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.252.149.15 - - [26/Feb/2008:21:08:27 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [26/Feb/2008:21:08:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.3.173.130 - - [26/Feb/2008:21:12:25 -0600] "GET /ply/ HTTP/1.1" 304 - +195.3.173.130 - - [26/Feb/2008:21:12:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +195.3.173.130 - - [26/Feb/2008:21:12:43 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 61381 +98.206.164.173 - - [26/Feb/2008:21:16:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +98.206.164.173 - - [26/Feb/2008:21:16:26 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +69.219.161.183 - - [26/Feb/2008:21:19:43 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +69.219.161.183 - - [26/Feb/2008:21:19:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.232.163.8 - - [26/Feb/2008:21:20:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialPerl5SharedLibraryExampleOnLinux HTTP/1.1" 200 2303 +99.232.163.8 - - [26/Feb/2008:21:21:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:21:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [26/Feb/2008:21:21:53 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.7.107 - - [26/Feb/2008:21:24:19 -0600] "GET /swill/ HTTP/1.0" 200 3786 +201.236.226.90 - - [26/Feb/2008:21:26:23 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [26/Feb/2008:21:26:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:21:26:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +201.236.226.90 - - [26/Feb/2008:21:26:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [26/Feb/2008:21:26:47 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 206 23851 +201.236.226.90 - - [26/Feb/2008:21:26:59 -0600] "GET /python.html HTTP/1.1" 200 18870 +201.236.226.90 - - [26/Feb/2008:21:27:03 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [26/Feb/2008:21:28:20 -0600] "GET /python.html HTTP/1.1" 200 18870 +208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [26/Feb/2008:21:28:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.5.217.3 - - [26/Feb/2008:21:36:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.5.217.3 - - [26/Feb/2008:21:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.164 - - [26/Feb/2008:21:41:59 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +204.111.252.233 - - [26/Feb/2008:21:42:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:21:44:56 -0600] "GET / HTTP/1.1" 200 4447 +70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +70.113.58.196 - - [26/Feb/2008:21:47:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:21:47:53 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +70.113.58.196 - - [26/Feb/2008:21:47:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.14.195.226 - - [26/Feb/2008:21:47:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +204.111.252.233 - - [26/Feb/2008:21:48:00 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +204.111.252.233 - - [26/Feb/2008:21:48:01 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +204.111.252.233 - - [26/Feb/2008:21:48:02 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +203.5.217.3 - - [26/Feb/2008:21:48:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.198.190.123 - - [26/Feb/2008:21:49:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.198.190.123 - - [26/Feb/2008:21:49:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.14.195.226 - - [26/Feb/2008:21:53:56 -0600] "GET /ply/README HTTP/1.1" 200 8605 +68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +68.165.56.5 - - [26/Feb/2008:21:54:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.165.56.5 - - [26/Feb/2008:21:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.165.56.5 - - [26/Feb/2008:21:54:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +74.6.26.147 - - [26/Feb/2008:21:56:50 -0600] "GET /photos/wind/pages/IMG_1331.htm HTTP/1.0" 404 133 +70.113.58.196 - - [26/Feb/2008:22:02:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.113.58.196 - - [26/Feb/2008:22:02:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.250.6.243 - - [26/Feb/2008:22:04:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.250.6.243 - - [26/Feb/2008:22:04:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET / HTTP/1.1" 200 4447 +67.165.221.14 - - [26/Feb/2008:22:05:31 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.165.221.14 - - [26/Feb/2008:22:05:51 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.165.221.14 - - [26/Feb/2008:22:06:29 -0600] "GET /software.html HTTP/1.1" 200 3163 +67.165.221.14 - - [26/Feb/2008:22:06:31 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.165.221.14 - - [26/Feb/2008:22:06:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +67.165.221.14 - - [26/Feb/2008:22:06:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /about.html HTTP/1.1" 200 7890 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +67.165.221.14 - - [26/Feb/2008:22:06:48 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +67.165.221.14 - - [26/Feb/2008:22:06:49 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +67.165.221.14 - - [26/Feb/2008:22:08:25 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.165.221.14 - - [26/Feb/2008:22:08:31 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +71.145.147.182 - - [26/Feb/2008:22:19:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +71.145.147.182 - - [26/Feb/2008:22:19:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:22:19:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:22:19:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.176 - - [26/Feb/2008:22:19:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.145.147.182 - - [26/Feb/2008:22:19:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +128.143.38.176 - - [26/Feb/2008:22:20:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.28.122 - - [26/Feb/2008:22:22:23 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [26/Feb/2008:22:29:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [26/Feb/2008:22:33:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.10 - - [26/Feb/2008:22:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.228.99 - - [26/Feb/2008:22:34:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.233.178.136 - - [26/Feb/2008:22:39:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 +163.29.130.55 - - [26/Feb/2008:22:39:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.124.85.80 - - [26/Feb/2008:22:40:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.80 - - [26/Feb/2008:22:42:26 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +222.153.72.162 - - [26/Feb/2008:22:43:28 -0600] "GET / HTTP/1.1" 200 4447 +128.143.117.220 - - [26/Feb/2008:22:45:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [26/Feb/2008:22:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.228.99 - - [26/Feb/2008:23:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.126.245.198 - - [26/Feb/2008:23:04:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.126.222.62 - - [26/Feb/2008:23:04:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.126.222.62 - - [26/Feb/2008:23:04:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +74.6.26.212 - - [26/Feb/2008:23:07:59 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.0" 200 85901 +65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [26/Feb/2008:23:09:54 -0600] "GET / HTTP/1.0" 200 4447 +65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [26/Feb/2008:23:19:45 -0600] "GET /training.html HTTP/1.1" 200 6154 +128.250.6.243 - - [26/Feb/2008:23:22:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.143.136.157 - - [26/Feb/2008:23:23:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [26/Feb/2008:23:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.250.6.243 - - [26/Feb/2008:23:26:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +128.250.6.243 - - [26/Feb/2008:23:26:48 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.0" 200 7962 +128.250.6.243 - - [26/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +217.172.44.82 - - [26/Feb/2008:23:27:02 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.25.20 - - [26/Feb/2008:23:28:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.176.147.11 - - [26/Feb/2008:23:29:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +67.176.147.11 - - [26/Feb/2008:23:34:13 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +98.206.164.173 - - [26/Feb/2008:23:41:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.206.164.173 - - [26/Feb/2008:23:44:04 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +65.214.45.129 - - [26/Feb/2008:23:49:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 +59.93.252.161 - - [26/Feb/2008:23:53:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +59.93.252.161 - - [26/Feb/2008:23:53:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:43 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.13 - - [27/Feb/2008:00:09:54 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +61.11.86.160 - - [27/Feb/2008:00:13:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +61.11.86.160 - - [27/Feb/2008:00:13:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.11.86.160 - - [27/Feb/2008:00:13:52 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +61.11.86.160 - - [27/Feb/2008:00:14:13 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +213.145.165.82 - - [27/Feb/2008:00:15:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.11.86.160 - - [27/Feb/2008:00:17:45 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.62.52.128 - - [27/Feb/2008:00:18:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.82.115.10 - - [27/Feb/2008:00:26:17 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.0" 200 188949 +67.195.58.169 - - [27/Feb/2008:00:26:53 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 304 - +74.6.20.121 - - [27/Feb/2008:00:29:28 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.0" 200 279116 +67.195.58.175 - - [27/Feb/2008:00:47:16 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 304 - +65.214.45.129 - - [27/Feb/2008:00:49:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +91.98.137.144 - - [27/Feb/2008:00:52:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.217.40.133 - - [27/Feb/2008:00:52:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +91.98.137.144 - - [27/Feb/2008:00:52:34 -0600] "GET / HTTP/1.1" 200 4447 +213.217.40.133 - - [27/Feb/2008:00:52:36 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +91.98.137.144 - - [27/Feb/2008:00:52:38 -0600] "GET /training.html HTTP/1.1" 200 6154 +91.98.137.144 - - [27/Feb/2008:00:52:43 -0600] "GET /index.html HTTP/1.1" 200 4447 +91.98.137.144 - - [27/Feb/2008:00:52:52 -0600] "GET /software.html HTTP/1.1" 200 3163 +91.98.137.144 - - [27/Feb/2008:00:53:14 -0600] "GET /ply/ply-1.0.tar.gz HTTP/1.1" 200 60130 +91.98.137.144 - - [27/Feb/2008:00:53:19 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.1" 200 62496 +91.98.137.144 - - [27/Feb/2008:00:53:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +122.164.159.157 - - [27/Feb/2008:00:55:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +122.164.159.157 - - [27/Feb/2008:00:55:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.39.203 - - [27/Feb/2008:01:03:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +41.232.39.203 - - [27/Feb/2008:01:03:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [27/Feb/2008:01:05:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.137.228.16 - - [27/Feb/2008:01:08:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 130588 +69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 161946 +69.137.228.16 - - [27/Feb/2008:01:09:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:01:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.206.102.103 - - [27/Feb/2008:01:16:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.206.102.103 - - [27/Feb/2008:01:16:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.206.102.103 - - [27/Feb/2008:01:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.160 - - [27/Feb/2008:01:22:43 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 304 - +199.111.224.90 - - [27/Feb/2008:01:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.65.240.234 - - [27/Feb/2008:01:28:22 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "HEAD /ply/ HTTP/1.0" 200 0 +38.104.7.146 - - [27/Feb/2008:01:28:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 +80.121.187.18 - - [27/Feb/2008:01:29:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +157.99.64.13 - - [27/Feb/2008:01:29:39 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.121.187.18 - - [27/Feb/2008:01:29:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +80.121.187.18 - - [27/Feb/2008:01:29:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.121.187.18 - - [27/Feb/2008:01:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +80.121.187.18 - - [27/Feb/2008:01:30:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +80.121.187.18 - - [27/Feb/2008:01:31:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +80.121.187.18 - - [27/Feb/2008:01:31:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +80.121.187.18 - - [27/Feb/2008:01:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MailingList HTTP/1.1" 200 1772 +76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.24.27.20 - - [27/Feb/2008:01:33:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.24.27.20 - - [27/Feb/2008:01:33:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.24.27.20 - - [27/Feb/2008:01:33:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +76.24.27.20 - - [27/Feb/2008:01:33:44 -0600] "GET /ply/README HTTP/1.1" 200 8605 +65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [27/Feb/2008:01:35:19 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 304 - +67.195.58.170 - - [27/Feb/2008:01:42:56 -0600] "GET / HTTP/1.0" 304 - +67.195.58.178 - - [27/Feb/2008:01:43:35 -0600] "GET /software.html HTTP/1.0" 200 3163 +67.195.58.185 - - [27/Feb/2008:01:44:22 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +65.55.212.77 - - [27/Feb/2008:01:48:02 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.20.75 - - [27/Feb/2008:02:00:30 -0600] "GET /papers/IPPS97/IPPS97.pdf HTTP/1.0" 304 - +130.76.32.182 - - [27/Feb/2008:02:00:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +130.76.32.182 - - [27/Feb/2008:02:00:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.76.32.182 - - [27/Feb/2008:02:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +130.76.32.182 - - [27/Feb/2008:02:00:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +130.76.32.182 - - [27/Feb/2008:02:01:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +130.76.32.182 - - [27/Feb/2008:02:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsDarwin HTTP/1.1" 200 1916 +130.76.32.182 - - [27/Feb/2008:02:01:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.54.165.36 - - [27/Feb/2008:02:02:35 -0600] "GET /python.html HTTP/1.1" 200 18870 +130.76.32.182 - - [27/Feb/2008:02:06:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +130.76.32.182 - - [27/Feb/2008:02:06:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +130.76.32.182 - - [27/Feb/2008:02:06:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +157.99.64.13 - - [27/Feb/2008:02:06:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +157.99.64.13 - - [27/Feb/2008:02:06:45 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +217.65.240.234 - - [27/Feb/2008:02:08:30 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +130.76.32.182 - - [27/Feb/2008:02:09:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.35.109.3 - - [27/Feb/2008:02:10:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.35.109.3 - - [27/Feb/2008:02:10:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [27/Feb/2008:02:13:14 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1122 +67.195.58.174 - - [27/Feb/2008:02:19:18 -0600] "GET /ply/ HTTP/1.0" 304 - +210.81.80.193 - - [27/Feb/2008:02:29:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.64.8.202 - - [27/Feb/2008:02:30:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +77.64.8.202 - - [27/Feb/2008:02:30:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [27/Feb/2008:02:32:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.80.245.157 - - [27/Feb/2008:02:36:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [27/Feb/2008:02:37:19 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +195.212.29.83 - - [27/Feb/2008:02:48:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.213.7.130 - - [27/Feb/2008:02:52:23 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.130 - - [27/Feb/2008:02:52:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.248.73.112 - - [27/Feb/2008:02:52:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +203.213.7.130 - - [27/Feb/2008:02:54:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.24.233 - - [27/Feb/2008:02:59:02 -0600] "GET /photos/wind/index.htm HTTP/1.0" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.21.242.91 - - [27/Feb/2008:03:02:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:17 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +87.194.174.76 - - [27/Feb/2008:03:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +80.149.16.155 - - [27/Feb/2008:03:12:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.155.221.86 - - [27/Feb/2008:03:14:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.149.251.212 - - [27/Feb/2008:03:16:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.149.251.212 - - [27/Feb/2008:03:16:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +88.149.251.212 - - [27/Feb/2008:03:16:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.1" 200 2813 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:03:22:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:03:23:04 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +24.1.159.241 - - [27/Feb/2008:03:24:10 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 +24.1.159.241 - - [27/Feb/2008:03:24:12 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +64.81.229.55 - - [27/Feb/2008:03:26:48 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +64.81.229.55 - - [27/Feb/2008:03:26:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.88.18.65 - - [27/Feb/2008:03:34:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.13.71.67 - - [27/Feb/2008:03:46:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.13.71.67 - - [27/Feb/2008:03:46:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:46:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +81.21.242.91 - - [27/Feb/2008:03:47:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.13.71.67 - - [27/Feb/2008:03:48:27 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +192.54.144.229 - - [27/Feb/2008:03:48:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [27/Feb/2008:03:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.24.228 - - [27/Feb/2008:03:53:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 +221.11.5.180 - - [27/Feb/2008:03:59:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.11.5.180 - - [27/Feb/2008:03:59:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.39.3.92 - - [27/Feb/2008:04:13:23 -0600] "GET / HTTP/1.1" 206 4447 +192.54.144.229 - - [27/Feb/2008:04:16:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.104.101.239 - - [27/Feb/2008:04:16:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 +124.104.101.239 - - [27/Feb/2008:04:17:59 -0600] "GET /python.html HTTP/1.0" 200 18870 +202.248.73.112 - - [27/Feb/2008:04:24:50 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +81.255.238.189 - - [27/Feb/2008:04:25:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.255.238.189 - - [27/Feb/2008:04:25:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.103.40.50 - - [27/Feb/2008:04:27:54 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +192.44.63.162 - - [27/Feb/2008:04:28:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.44.63.162 - - [27/Feb/2008:04:28:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.210.2.219 - - [27/Feb/2008:04:30:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.210.2.219 - - [27/Feb/2008:04:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.165.73.3 - - [27/Feb/2008:04:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.103.40.50 - - [27/Feb/2008:04:47:27 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +74.6.20.163 - - [27/Feb/2008:04:51:53 -0600] "GET /ply/ HTTP/1.0" 200 8018 +195.212.29.83 - - [27/Feb/2008:05:01:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.76.66.64 - - [27/Feb/2008:05:05:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.76.66.64 - - [27/Feb/2008:05:05:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.76.66.64 - - [27/Feb/2008:05:05:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.76.66.64 - - [27/Feb/2008:05:05:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +90.80.39.41 - - [27/Feb/2008:05:06:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.80.39.41 - - [27/Feb/2008:05:06:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.127.188.96 - - [27/Feb/2008:05:22:11 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +85.127.188.96 - - [27/Feb/2008:05:22:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.212.29.75 - - [27/Feb/2008:05:22:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:05:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:30:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:30:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.40.255.154 - - [27/Feb/2008:05:31:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:31:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +128.40.255.154 - - [27/Feb/2008:05:33:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.97.106.98 - - [27/Feb/2008:05:35:37 -0600] "GET /cgi-bin/wiki.pl?action=editprefs HTTP/1.1" 200 4261 +194.97.106.98 - - [27/Feb/2008:05:35:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.55.52.1 - - [27/Feb/2008:05:35:45 -0600] "GET /ply/ HTTP/1.0" 200 8018 +192.55.52.1 - - [27/Feb/2008:05:35:46 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +192.55.52.1 - - [27/Feb/2008:05:35:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +192.55.52.1 - - [27/Feb/2008:05:36:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +194.97.106.98 - - [27/Feb/2008:05:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.80.39.41 - - [27/Feb/2008:05:36:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +90.80.39.41 - - [27/Feb/2008:05:37:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +192.55.52.1 - - [27/Feb/2008:05:37:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +90.80.39.41 - - [27/Feb/2008:05:37:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +90.80.39.41 - - [27/Feb/2008:05:37:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +192.55.52.1 - - [27/Feb/2008:05:40:19 -0600] "GET /ply/README HTTP/1.0" 200 8605 +66.232.113.194 - - [27/Feb/2008:05:41:09 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2748 +201.55.193.26 - - [27/Feb/2008:05:41:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +161.53.125.15 - - [27/Feb/2008:05:41:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +87.101.244.6 - - [27/Feb/2008:05:41:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:41:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:42:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.124.252.210 - - [27/Feb/2008:05:42:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.43.200.236 - - [27/Feb/2008:05:44:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [27/Feb/2008:05:45:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:05:45:49 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.1" 304 - +193.3.39.1 - - [27/Feb/2008:05:48:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.3.39.1 - - [27/Feb/2008:05:48:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.151.18.10 - - [27/Feb/2008:05:49:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.151.18.10 - - [27/Feb/2008:05:49:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.53.125.15 - - [27/Feb/2008:05:54:13 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +161.53.125.15 - - [27/Feb/2008:05:54:14 -0600] "GET /cgi-bin/wiki.pl?ImportDirective HTTP/1.1" 200 2215 +193.3.39.1 - - [27/Feb/2008:05:54:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.255.238.189 - - [27/Feb/2008:06:02:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +206.51.237.114 - - [27/Feb/2008:06:06:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +60.28.31.194 - - [27/Feb/2008:06:06:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +189.56.68.138 - - [27/Feb/2008:06:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +67.195.58.158 - - [27/Feb/2008:06:25:02 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +222.122.236.43 - - [27/Feb/2008:06:26:38 -0600] "GET /robots.txt HTTP/1.1" 200 71 +90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +90.185.65.84 - - [27/Feb/2008:06:26:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.185.65.84 - - [27/Feb/2008:06:27:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +90.185.65.84 - - [27/Feb/2008:06:27:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MzScheme HTTP/1.1" 200 2865 +220.23.136.184 - - [27/Feb/2008:06:34:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.23.136.184 - - [27/Feb/2008:06:34:52 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.233.178.136 - - [27/Feb/2008:06:34:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +220.23.136.184 - - [27/Feb/2008:06:35:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +84.110.148.125 - - [27/Feb/2008:06:44:30 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.148.125 - - [27/Feb/2008:06:44:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +193.112.172.10 - - [27/Feb/2008:06:46:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +80.169.151.100 - - [27/Feb/2008:06:46:35 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.112.172.10 - - [27/Feb/2008:06:46:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 +193.112.172.10 - - [27/Feb/2008:06:47:18 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +193.112.172.10 - - [27/Feb/2008:06:47:33 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.0" 200 3248 +193.112.172.10 - - [27/Feb/2008:06:47:49 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +203.213.7.133 - - [27/Feb/2008:06:48:44 -0600] "GET /ply/ HTTP/1.0" 200 8018 +203.213.7.133 - - [27/Feb/2008:06:48:45 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.133 - - [27/Feb/2008:06:48:46 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +80.156.46.53 - - [27/Feb/2008:06:49:24 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.213.7.133 - - [27/Feb/2008:06:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +203.213.7.133 - - [27/Feb/2008:06:51:16 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +203.213.7.133 - - [27/Feb/2008:06:53:12 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.236.226.90 - - [27/Feb/2008:06:56:56 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [27/Feb/2008:06:56:58 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [27/Feb/2008:06:56:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:06:57:00 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +62.181.186.82 - - [27/Feb/2008:07:00:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +62.181.186.82 - - [27/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +118.172.30.90 - - [27/Feb/2008:07:01:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 80157 +201.24.117.154 - - [27/Feb/2008:07:01:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.24.117.154 - - [27/Feb/2008:07:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.24.117.154 - - [27/Feb/2008:07:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:07:02:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.181.186.82 - - [27/Feb/2008:07:03:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.252.247.8 - - [27/Feb/2008:07:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.12.88.244 - - [27/Feb/2008:07:13:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +89.12.88.244 - - [27/Feb/2008:07:13:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:14:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.133.202 - - [27/Feb/2008:07:14:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.13.133.202 - - [27/Feb/2008:07:14:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:15:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [27/Feb/2008:07:15:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:15:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.13.133.202 - - [27/Feb/2008:07:18:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [27/Feb/2008:07:20:53 -0600] "GET /photos/u505/pages/IMG_1490.htm HTTP/1.0" 404 133 +84.110.191.75 - - [27/Feb/2008:07:22:04 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.191.75 - - [27/Feb/2008:07:22:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET / HTTP/1.1" 200 4447 +68.166.120.178 - - [27/Feb/2008:07:29:32 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.166.120.178 - - [27/Feb/2008:07:30:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +68.166.120.178 - - [27/Feb/2008:07:30:19 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +217.65.240.234 - - [27/Feb/2008:07:30:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +122.117.168.219 - - [27/Feb/2008:07:30:44 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /about.html HTTP/1.1" 200 7890 +68.166.120.178 - - [27/Feb/2008:07:31:52 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +68.166.120.178 - - [27/Feb/2008:07:31:53 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +68.166.120.178 - - [27/Feb/2008:07:31:54 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +68.166.120.178 - - [27/Feb/2008:07:34:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +68.166.120.178 - - [27/Feb/2008:07:35:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +189.13.133.202 - - [27/Feb/2008:07:36:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.166.120.178 - - [27/Feb/2008:07:36:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.166.120.178 - - [27/Feb/2008:07:37:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +61.57.130.42 - - [27/Feb/2008:07:42:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.57.130.42 - - [27/Feb/2008:07:42:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +61.57.130.42 - - [27/Feb/2008:07:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.130.42 - - [27/Feb/2008:07:42:49 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /robots.txt HTTP/1.0" 200 71 +205.196.222.10 - - [27/Feb/2008:07:49:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +212.124.252.210 - - [27/Feb/2008:07:50:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +61.57.130.42 - - [27/Feb/2008:07:53:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +217.196.43.134 - - [27/Feb/2008:08:05:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.71.101.182 - - [27/Feb/2008:08:06:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.71.101.182 - - [27/Feb/2008:08:06:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:38 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.54.192.10 - - [27/Feb/2008:08:09:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.54.192.10 - - [27/Feb/2008:08:09:47 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +194.186.83.193 - - [27/Feb/2008:08:12:08 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +194.186.83.193 - - [27/Feb/2008:08:12:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:20:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +58.207.152.130 - - [27/Feb/2008:08:20:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +58.207.152.130 - - [27/Feb/2008:08:21:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +58.207.152.130 - - [27/Feb/2008:08:21:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +58.207.152.130 - - [27/Feb/2008:08:21:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.177.245 - - [27/Feb/2008:08:22:09 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.245 - - [27/Feb/2008:08:22:10 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1098 +202.163.114.53 - - [27/Feb/2008:08:23:09 -0600] "GET /cv.html HTTP/1.1" 200 31798 +189.31.124.147 - - [27/Feb/2008:08:27:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.88.162.35 - - [27/Feb/2008:08:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +189.31.124.147 - - [27/Feb/2008:08:27:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.31.124.147 - - [27/Feb/2008:08:27:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +144.204.65.6 - - [27/Feb/2008:08:29:01 -0600] "GET /ply/ HTTP/1.1" 200 8018 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.204.65.6 - - [27/Feb/2008:08:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +189.31.124.147 - - [27/Feb/2008:08:29:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:29:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.14.110.6 - - [27/Feb/2008:08:29:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.14.110.6 - - [27/Feb/2008:08:29:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.14.110.6 - - [27/Feb/2008:08:29:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.24.117.154 - - [27/Feb/2008:08:31:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.14.110.6 - - [27/Feb/2008:08:34:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [27/Feb/2008:08:34:55 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.255.240.194 - - [27/Feb/2008:08:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.255.240.194 - - [27/Feb/2008:08:36:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +62.255.240.194 - - [27/Feb/2008:08:36:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +62.255.240.194 - - [27/Feb/2008:08:36:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +60.176.145.165 - - [27/Feb/2008:08:37:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.255.240.194 - - [27/Feb/2008:08:37:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:08:40:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.40.255.154 - - [27/Feb/2008:08:41:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.186.27.193 - - [27/Feb/2008:08:45:03 -0600] "GET /ply/ HTTP/1.1" 200 8018 +91.186.27.193 - - [27/Feb/2008:08:45:04 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.223.13.234 - - [27/Feb/2008:08:46:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +76.223.13.234 - - [27/Feb/2008:08:48:13 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +74.6.25.142 - - [27/Feb/2008:08:48:52 -0600] "GET /python/tutorial/ HTTP/1.0" 403 214 +128.135.212.179 - - [27/Feb/2008:08:49:23 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.212.179 - - [27/Feb/2008:08:51:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.246.7.155 - - [27/Feb/2008:08:58:18 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.246.7.155 - - [27/Feb/2008:08:58:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.121.236.201 - - [27/Feb/2008:08:58:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.121.236.201 - - [27/Feb/2008:08:58:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.121.236.201 - - [27/Feb/2008:08:58:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.246.7.155 - - [27/Feb/2008:08:59:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +98.206.164.173 - - [27/Feb/2008:09:04:16 -0600] "GET /dynamic/ HTTP/1.1" 304 - +89.165.73.133 - - [27/Feb/2008:09:04:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.59.141.172 - - [27/Feb/2008:09:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.59.141.172 - - [27/Feb/2008:09:05:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.161.65.194 - - [27/Feb/2008:09:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.124.13.183 - - [27/Feb/2008:09:13:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +85.124.13.183 - - [27/Feb/2008:09:13:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.43.32.87 - - [27/Feb/2008:09:14:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.165.73.133 - - [27/Feb/2008:09:16:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:09:17:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.187.74 - - [27/Feb/2008:09:27:57 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [27/Feb/2008:09:27:59 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1391 +150.210.155.167 - - [27/Feb/2008:09:29:40 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.236.90.186 - - [27/Feb/2008:09:30:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.236.90.186 - - [27/Feb/2008:09:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.236.90.186 - - [27/Feb/2008:09:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +74.6.20.147 - - [27/Feb/2008:09:34:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 +192.54.144.229 - - [27/Feb/2008:09:35:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.245.138.101 - - [27/Feb/2008:09:37:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.245.138.101 - - [27/Feb/2008:09:37:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.245.138.101 - - [27/Feb/2008:09:37:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.74.100.50 - - [27/Feb/2008:09:38:15 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.122.102.16 - - [27/Feb/2008:09:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.122.102.16 - - [27/Feb/2008:09:40:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.122.102.16 - - [27/Feb/2008:09:40:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.122.102.16 - - [27/Feb/2008:09:40:44 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 19833 +87.122.102.16 - - [27/Feb/2008:09:40:55 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +87.122.102.16 - - [27/Feb/2008:09:41:01 -0600] "GET /ply/ply.html HTTP/1.1" 206 90365 +124.161.65.194 - - [27/Feb/2008:09:42:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +124.161.65.194 - - [27/Feb/2008:09:42:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.19 - - [27/Feb/2008:09:43:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +124.161.65.194 - - [27/Feb/2008:09:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +155.140.133.62 - - [27/Feb/2008:09:43:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +75.24.211.116 - - [27/Feb/2008:09:43:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.24.211.116 - - [27/Feb/2008:09:43:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.24.211.116 - - [27/Feb/2008:09:43:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +155.140.133.62 - - [27/Feb/2008:09:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.157.248.214 - - [27/Feb/2008:09:55:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.30.192.187 - - [27/Feb/2008:09:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.63.137.190 - - [27/Feb/2008:10:01:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.63.137.190 - - [27/Feb/2008:10:01:28 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 923 +212.63.137.190 - - [27/Feb/2008:10:01:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1009 +212.63.137.190 - - [27/Feb/2008:10:01:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/ HTTP/1.0" 200 8018 +141.115.28.2 - - [27/Feb/2008:10:02:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +141.115.28.2 - - [27/Feb/2008:10:02:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.219.135.118 - - [27/Feb/2008:10:03:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.219.135.118 - - [27/Feb/2008:10:03:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.219.135.118 - - [27/Feb/2008:10:04:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [27/Feb/2008:10:07:34 -0600] "GET /ply/ HTTP/1.0" 304 - +133.9.245.73 - - [27/Feb/2008:10:07:38 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +67.151.52.19 - - [27/Feb/2008:10:16:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.151.52.19 - - [27/Feb/2008:10:16:07 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +209.159.33.99 - - [27/Feb/2008:10:21:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +209.159.33.99 - - [27/Feb/2008:10:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +157.157.91.18 - - [27/Feb/2008:10:25:22 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +66.249.65.37 - - [27/Feb/2008:10:29:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.80.231 - - [27/Feb/2008:10:29:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.80.231 - - [27/Feb/2008:10:29:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /ply/README HTTP/1.1" 200 8605 +69.218.90.173 - - [27/Feb/2008:10:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply HTTP/1.1" 301 242 +69.218.90.173 - - [27/Feb/2008:10:32:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.218.90.173 - - [27/Feb/2008:10:32:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.218.90.173 - - [27/Feb/2008:10:33:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +204.130.247.244 - - [27/Feb/2008:10:37:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +24.7.210.64 - - [27/Feb/2008:10:40:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.7.210.64 - - [27/Feb/2008:10:40:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.209.249.3 - - [27/Feb/2008:10:41:05 -0600] "GET /python.html HTTP/1.1" 200 18870 +205.209.249.3 - - [27/Feb/2008:10:41:06 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +41.219.193.49 - - [27/Feb/2008:10:44:25 -0600] "GET / HTTP/1.1" 200 4447 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +141.142.240.56 - - [27/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:50:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.234.244.162 - - [27/Feb/2008:10:50:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [27/Feb/2008:10:50:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:50:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +141.142.240.56 - - [27/Feb/2008:10:52:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.216.13.202 - - [27/Feb/2008:10:53:04 -0600] "GET /ply HTTP/1.1" 301 242 +68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.216.13.202 - - [27/Feb/2008:10:53:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.216.13.202 - - [27/Feb/2008:10:53:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +141.142.240.56 - - [27/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +198.175.55.5 - - [27/Feb/2008:10:54:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +198.175.55.5 - - [27/Feb/2008:10:54:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.175.55.5 - - [27/Feb/2008:10:54:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.9.243.111 - - [27/Feb/2008:10:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.9.243.111 - - [27/Feb/2008:10:56:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +198.175.55.5 - - [27/Feb/2008:10:56:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [27/Feb/2008:10:57:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [27/Feb/2008:10:57:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +204.39.56.160 - - [27/Feb/2008:10:58:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.53.65.44 - - [27/Feb/2008:11:00:56 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +84.110.205.221 - - [27/Feb/2008:11:02:29 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.205.221 - - [27/Feb/2008:11:02:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +89.165.73.133 - - [27/Feb/2008:11:04:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:04:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.59.123.114 - - [27/Feb/2008:11:06:59 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.59.123.114 - - [27/Feb/2008:11:07:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.59.123.114 - - [27/Feb/2008:11:07:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +89.165.73.133 - - [27/Feb/2008:11:09:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:09:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.133 - - [27/Feb/2008:11:09:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.214.175.76 - - [27/Feb/2008:11:09:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.196.80.231 - - [27/Feb/2008:11:11:46 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.221.26.30 - - [27/Feb/2008:11:16:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.221.26.30 - - [27/Feb/2008:11:16:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:11:18:45 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +201.236.226.90 - - [27/Feb/2008:11:19:12 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +128.196.80.231 - - [27/Feb/2008:11:22:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 +200.55.140.181 - - [27/Feb/2008:11:27:22 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +200.55.140.181 - - [27/Feb/2008:11:27:23 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +74.6.28.45 - - [27/Feb/2008:11:33:12 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 +128.196.80.231 - - [27/Feb/2008:11:33:38 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +83.24.38.176 - - [27/Feb/2008:11:37:57 -0600] "GET /ply/ HTTP/1.1" 304 - +83.24.38.176 - - [27/Feb/2008:11:38:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +83.24.38.176 - - [27/Feb/2008:11:43:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.24.38.176 - - [27/Feb/2008:11:43:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.24.38.176 - - [27/Feb/2008:11:43:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +86.141.0.114 - - [27/Feb/2008:11:51:13 -0600] "GET /ply/ HTTP/1.1" 200 8018 +86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +86.141.0.114 - - [27/Feb/2008:11:51:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:51:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:51:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:11:52:10 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +24.1.159.241 - - [27/Feb/2008:11:57:55 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +87.221.119.235 - - [27/Feb/2008:11:58:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +87.221.119.235 - - [27/Feb/2008:11:58:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.221.119.235 - - [27/Feb/2008:11:58:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +190.199.163.61 - - [27/Feb/2008:12:06:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.199.163.61 - - [27/Feb/2008:12:06:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /python.html HTTP/1.1" 200 18870 +80.191.172.9 - - [27/Feb/2008:12:13:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.191.172.9 - - [27/Feb/2008:12:14:00 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +204.246.129.196 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [27/Feb/2008:12:19:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +74.6.25.20 - - [27/Feb/2008:12:21:42 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.25.22 - - [27/Feb/2008:12:21:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 +74.6.26.235 - - [27/Feb/2008:12:21:48 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1582 +128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.196.205.39 - - [27/Feb/2008:12:33:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.118 - - [27/Feb/2008:12:35:24 -0600] "GET /photos/wind/pages/IMG_1253.htm HTTP/1.1" 404 133 +128.196.205.39 - - [27/Feb/2008:12:36:06 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +209.17.146.129 - - [27/Feb/2008:12:44:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.228.97.106 - - [27/Feb/2008:12:47:46 -0600] "HEAD /ply/ HTTP/1.0" 200 0 +84.110.209.164 - - [27/Feb/2008:12:47:47 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.209.164 - - [27/Feb/2008:12:47:48 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1032 +67.228.97.106 - - [27/Feb/2008:12:48:23 -0600] "HEAD /ply/example.html HTTP/1.0" 304 - +76.185.24.146 - - [27/Feb/2008:12:49:54 -0600] "GET /python.html HTTP/1.1" 200 18870 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.185.24.146 - - [27/Feb/2008:12:49:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.185.24.146 - - [27/Feb/2008:12:52:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:12:52:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +76.185.24.146 - - [27/Feb/2008:12:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.162.120.220 - - [27/Feb/2008:12:56:06 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +76.185.24.146 - - [27/Feb/2008:12:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.23.41 - - [27/Feb/2008:12:59:13 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1336 +76.185.24.146 - - [27/Feb/2008:12:59:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +134.173.88.178 - - [27/Feb/2008:13:01:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.88.178 - - [27/Feb/2008:13:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.186.48.152 - - [27/Feb/2008:13:02:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.186.48.152 - - [27/Feb/2008:13:02:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.32.37.138 - - [27/Feb/2008:13:02:59 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.87.72.51 - - [27/Feb/2008:13:03:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 +76.185.24.146 - - [27/Feb/2008:13:11:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:28 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.5.113.131 - - [27/Feb/2008:13:13:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +71.5.113.131 - - [27/Feb/2008:13:13:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +74.6.26.145 - - [27/Feb/2008:13:14:05 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1322 +134.173.88.178 - - [27/Feb/2008:13:16:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.160.138.141 - - [27/Feb/2008:13:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +144.51.43.161 - - [27/Feb/2008:13:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +144.51.43.161 - - [27/Feb/2008:13:23:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +144.51.43.161 - - [27/Feb/2008:13:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +144.51.43.161 - - [27/Feb/2008:13:24:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +144.51.43.161 - - [27/Feb/2008:13:24:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +144.51.43.161 - - [27/Feb/2008:13:25:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +195.214.232.10 - - [27/Feb/2008:13:32:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.214.232.10 - - [27/Feb/2008:13:32:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +151.48.8.237 - - [27/Feb/2008:13:32:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.48.8.237 - - [27/Feb/2008:13:32:59 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.205.190 - - [27/Feb/2008:13:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:15 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.190 - - [27/Feb/2008:13:38:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.198.239.27 - - [27/Feb/2008:13:40:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.123 - - [27/Feb/2008:13:41:55 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +88.191.19.81 - - [27/Feb/2008:13:42:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.198.239.27 - - [27/Feb/2008:13:42:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.179.69 - - [27/Feb/2008:13:44:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.179.69 - - [27/Feb/2008:13:44:34 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.173.88.178 - - [27/Feb/2008:13:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +68.191.255.98 - - [27/Feb/2008:13:47:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.191.255.98 - - [27/Feb/2008:13:47:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +68.191.255.98 - - [27/Feb/2008:13:47:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +68.191.255.98 - - [27/Feb/2008:13:47:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.191.255.98 - - [27/Feb/2008:13:47:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +76.171.199.78 - - [27/Feb/2008:13:48:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.171.199.78 - - [27/Feb/2008:13:48:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.171.199.78 - - [27/Feb/2008:13:49:31 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +12.107.176.254 - - [27/Feb/2008:13:50:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.200.69 - - [27/Feb/2008:13:52:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [27/Feb/2008:13:58:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.218.75.11 - - [27/Feb/2008:13:59:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +70.218.75.11 - - [27/Feb/2008:13:59:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.195.58.158 - - [27/Feb/2008:14:00:43 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +64.234.244.162 - - [27/Feb/2008:14:01:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.171.199.78 - - [27/Feb/2008:14:02:37 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET / HTTP/1.1" 200 4447 +128.135.125.239 - - [27/Feb/2008:14:03:45 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /python.html HTTP/1.1" 200 18870 +128.135.125.239 - - [27/Feb/2008:14:04:49 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +84.110.138.116 - - [27/Feb/2008:14:08:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.138.116 - - [27/Feb/2008:14:08:57 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 972 +171.66.35.216 - - [27/Feb/2008:14:12:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +171.66.35.216 - - [27/Feb/2008:14:12:37 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +24.15.187.198 - - [27/Feb/2008:14:13:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.117.220 - - [27/Feb/2008:14:14:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [27/Feb/2008:14:14:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.117.220 - - [27/Feb/2008:14:14:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +129.55.200.20 - - [27/Feb/2008:14:15:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +129.55.200.20 - - [27/Feb/2008:14:16:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqTutorialJavaSharedLibraryExampleOnLinux HTTP/1.0" 200 2801 +128.143.117.220 - - [27/Feb/2008:14:16:41 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.117.220 - - [27/Feb/2008:14:16:42 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 145565 +86.206.17.195 - - [27/Feb/2008:14:19:25 -0600] "GET / HTTP/1.1" 200 4447 +86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +86.206.17.195 - - [27/Feb/2008:14:19:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.206.17.195 - - [27/Feb/2008:14:19:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 +86.206.17.195 - - [27/Feb/2008:14:19:33 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +199.111.200.69 - - [27/Feb/2008:14:26:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.44.110 - - [27/Feb/2008:14:26:06 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [27/Feb/2008:14:26:06 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.31.151 - - [27/Feb/2008:14:28:16 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +24.15.187.198 - - [27/Feb/2008:14:30:33 -0600] "GET /dynamic/ HTTP/1.1" 304 - +24.15.187.198 - - [27/Feb/2008:14:30:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.14.206.105 - - [27/Feb/2008:14:36:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.14.206.105 - - [27/Feb/2008:14:36:41 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +88.165.126.62 - - [27/Feb/2008:14:39:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.165.126.62 - - [27/Feb/2008:14:39:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.165.126.62 - - [27/Feb/2008:14:39:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.6.151.80 - - [27/Feb/2008:14:40:31 -0600] "GET /robots.txt HTTP/1.0" 200 71 +24.2.76.175 - - [27/Feb/2008:14:45:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.2.76.175 - - [27/Feb/2008:14:45:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +155.91.28.232 - - [27/Feb/2008:14:47:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.15.187.198 - - [27/Feb/2008:14:54:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +84.134.8.241 - - [27/Feb/2008:14:56:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.134.8.241 - - [27/Feb/2008:14:56:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:56:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:56:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.134.8.241 - - [27/Feb/2008:14:57:25 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.15.187.198 - - [27/Feb/2008:15:01:44 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +220.239.245.127 - - [27/Feb/2008:15:01:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.239.245.127 - - [27/Feb/2008:15:01:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +220.239.245.127 - - [27/Feb/2008:15:01:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [27/Feb/2008:15:05:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.15.187.198 - - [27/Feb/2008:15:06:22 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +88.67.224.55 - - [27/Feb/2008:15:10:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.67.224.55 - - [27/Feb/2008:15:11:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +88.67.224.55 - - [27/Feb/2008:15:13:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.124 - - [27/Feb/2008:15:15:08 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:15:15:56 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:15:15:57 -0600] "GET / HTTP/1.1" 200 4447 +64.0.160.210 - - [27/Feb/2008:15:17:33 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +64.0.160.210 - - [27/Feb/2008:15:17:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply HTTP/1.1" 301 242 +64.0.160.210 - - [27/Feb/2008:15:18:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.0.160.210 - - [27/Feb/2008:15:18:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.0.160.210 - - [27/Feb/2008:15:18:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +200.11.208.122 - - [27/Feb/2008:15:20:01 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +200.11.208.122 - - [27/Feb/2008:15:20:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.13 - - [27/Feb/2008:15:22:32 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +77.91.224.3 - - [27/Feb/2008:15:22:57 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.3 - - [27/Feb/2008:15:22:58 -0600] "GET /swill/ HTTP/1.1" 200 3786 +77.91.224.13 - - [27/Feb/2008:15:24:51 -0600] "GET /publications.html HTTP/1.1" 200 7758 +89.168.18.6 - - [27/Feb/2008:15:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +89.168.18.6 - - [27/Feb/2008:15:26:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:26:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:26:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +77.91.224.3 - - [27/Feb/2008:15:26:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 +89.168.18.6 - - [27/Feb/2008:15:26:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +89.168.18.6 - - [27/Feb/2008:15:27:50 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +89.168.18.6 - - [27/Feb/2008:15:28:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +77.91.224.13 - - [27/Feb/2008:15:28:03 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +89.168.18.6 - - [27/Feb/2008:15:28:09 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +89.168.18.6 - - [27/Feb/2008:15:28:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +89.168.18.6 - - [27/Feb/2008:15:28:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +77.91.224.3 - - [27/Feb/2008:15:28:35 -0600] "GET /diet.html HTTP/1.1" 404 133 +89.168.18.6 - - [27/Feb/2008:15:28:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +77.91.224.3 - - [27/Feb/2008:15:29:27 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +89.168.18.6 - - [27/Feb/2008:15:29:30 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +89.168.18.6 - - [27/Feb/2008:15:29:50 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +89.168.18.6 - - [27/Feb/2008:15:30:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +89.168.18.6 - - [27/Feb/2008:15:30:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +89.168.18.6 - - [27/Feb/2008:15:30:35 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +89.168.18.6 - - [27/Feb/2008:15:30:39 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +77.91.224.13 - - [27/Feb/2008:15:31:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +77.91.224.3 - - [27/Feb/2008:15:31:45 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +77.91.224.3 - - [27/Feb/2008:15:32:37 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +77.91.224.13 - - [27/Feb/2008:15:34:18 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +77.91.224.3 - - [27/Feb/2008:15:34:58 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +66.249.65.37 - - [27/Feb/2008:15:36:30 -0600] "GET / HTTP/1.1" 304 - +77.91.224.13 - - [27/Feb/2008:15:36:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +77.91.224.3 - - [27/Feb/2008:15:38:12 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [27/Feb/2008:15:39:26 -0600] "GET / HTTP/1.1" 200 4447 +66.249.65.37 - - [27/Feb/2008:15:42:08 -0600] "GET /python.html HTTP/1.1" 304 - +66.249.65.37 - - [27/Feb/2008:15:43:17 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +209.198.142.114 - - [27/Feb/2008:15:44:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +209.198.142.114 - - [27/Feb/2008:15:44:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.202.49.172 - - [27/Feb/2008:15:46:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +67.202.49.172 - - [27/Feb/2008:15:46:56 -0600] "GET /publications.html HTTP/1.1" 200 7758 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +67.202.49.172 - - [27/Feb/2008:15:46:57 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +209.198.142.114 - - [27/Feb/2008:15:48:17 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +209.198.142.114 - - [27/Feb/2008:15:48:30 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +66.249.65.37 - - [27/Feb/2008:15:48:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.198.142.114 - - [27/Feb/2008:15:48:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +209.198.142.114 - - [27/Feb/2008:15:49:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +209.198.142.114 - - [27/Feb/2008:15:50:08 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +209.198.142.114 - - [27/Feb/2008:15:50:18 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +80.229.70.194 - - [27/Feb/2008:15:51:36 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.70.194 - - [27/Feb/2008:15:51:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.70.194 - - [27/Feb/2008:15:51:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.191.19.81 - - [27/Feb/2008:15:51:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +67.202.49.172 - - [27/Feb/2008:15:52:13 -0600] "GET /diet.html HTTP/1.1" 404 133 +67.202.49.172 - - [27/Feb/2008:15:52:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 +65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [27/Feb/2008:15:52:26 -0600] "GET /photos/wind/ThumbnailFrame.htm HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:15:54:20 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.67.242.79 - - [27/Feb/2008:15:57:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.67.242.79 - - [27/Feb/2008:15:57:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.221 - - [27/Feb/2008:15:58:32 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +87.194.206.16 - - [27/Feb/2008:15:58:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +87.194.206.16 - - [27/Feb/2008:15:58:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.115 - - [27/Feb/2008:15:59:11 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [27/Feb/2008:15:59:11 -0600] "GET /ply/ HTTP/1.0" 200 8018 +66.249.65.37 - - [27/Feb/2008:15:59:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 +87.194.206.16 - - [27/Feb/2008:15:59:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [27/Feb/2008:16:01:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:16:01:43 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.1" 304 - +74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [27/Feb/2008:16:02:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +87.194.206.16 - - [27/Feb/2008:16:03:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.92.109.170 - - [27/Feb/2008:16:04:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +84.92.109.170 - - [27/Feb/2008:16:04:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.92.109.170 - - [27/Feb/2008:16:04:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +84.92.109.170 - - [27/Feb/2008:16:04:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +84.92.109.170 - - [27/Feb/2008:16:04:47 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +66.249.65.37 - - [27/Feb/2008:16:05:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.204.154 - - [27/Feb/2008:16:11:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.204.154 - - [27/Feb/2008:16:11:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.204.154 - - [27/Feb/2008:16:11:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.160.75.249 - - [27/Feb/2008:16:12:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.160.75.249 - - [27/Feb/2008:16:12:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +216.160.75.249 - - [27/Feb/2008:16:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +216.160.75.249 - - [27/Feb/2008:16:12:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +216.160.75.249 - - [27/Feb/2008:16:13:10 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +216.160.75.249 - - [27/Feb/2008:16:13:13 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +71.201.41.248 - - [27/Feb/2008:16:16:47 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +71.201.41.248 - - [27/Feb/2008:16:16:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [27/Feb/2008:16:16:59 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.135.125.239 - - [27/Feb/2008:16:17:53 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +24.1.159.241 - - [27/Feb/2008:16:23:55 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:16:23:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [27/Feb/2008:16:24:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +89.168.18.6 - - [27/Feb/2008:16:24:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.168.18.6 - - [27/Feb/2008:16:24:50 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +89.168.18.6 - - [27/Feb/2008:16:25:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +89.168.18.6 - - [27/Feb/2008:16:25:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +89.168.18.6 - - [27/Feb/2008:16:25:16 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +65.55.212.77 - - [27/Feb/2008:16:25:29 -0600] "GET /robots.txt HTTP/1.0" 200 71 +84.75.247.28 - - [27/Feb/2008:16:27:33 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 15592 +84.75.247.28 - - [27/Feb/2008:16:27:44 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 +99.226.226.178 - - [27/Feb/2008:16:31:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:16:31:56 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 304 - +74.6.26.159 - - [27/Feb/2008:16:32:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE061.HTM HTTP/1.0" 304 - +128.221.197.20 - - [27/Feb/2008:16:34:07 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +87.205.225.143 - - [27/Feb/2008:16:42:46 -0600] "GET /ply/ HTTP/1.0" 304 - +24.99.94.177 - - [27/Feb/2008:16:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.99.94.177 - - [27/Feb/2008:16:43:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.194.136.18 - - [27/Feb/2008:16:45:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.248.3 - - [27/Feb/2008:16:46:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.248.3 - - [27/Feb/2008:16:46:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [27/Feb/2008:16:46:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.173.185.186 - - [27/Feb/2008:16:47:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.232.113.194 - - [27/Feb/2008:16:49:35 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +202.106.212.226 - - [27/Feb/2008:16:49:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +66.249.65.37 - - [27/Feb/2008:16:49:54 -0600] "GET /index.html HTTP/1.1" 304 - +74.6.7.110 - - [27/Feb/2008:16:58:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 +199.111.224.90 - - [27/Feb/2008:16:58:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.19.201 - - [27/Feb/2008:17:00:39 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.19.201 - - [27/Feb/2008:17:00:40 -0600] "GET /ply/ HTTP/1.0" 200 8018 +134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.173.59.157 - - [27/Feb/2008:17:02:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.59.157 - - [27/Feb/2008:17:02:34 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1036 +134.173.59.157 - - [27/Feb/2008:17:02:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +134.173.59.157 - - [27/Feb/2008:17:11:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.173.59.157 - - [27/Feb/2008:17:11:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +134.173.59.157 - - [27/Feb/2008:17:11:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:11:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:12:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:12:15 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +24.10.16.193 - - [27/Feb/2008:17:17:30 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [27/Feb/2008:17:17:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:17:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:18:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:19:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [27/Feb/2008:17:20:58 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +206.51.237.114 - - [27/Feb/2008:17:22:30 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +203.113.115.20 - - [27/Feb/2008:17:22:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [27/Feb/2008:17:22:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.67.242.79 - - [27/Feb/2008:17:23:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +134.173.59.157 - - [27/Feb/2008:17:25:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +210.9.32.205 - - [27/Feb/2008:17:25:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.9.32.205 - - [27/Feb/2008:17:25:51 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.9.32.205 - - [27/Feb/2008:17:25:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [27/Feb/2008:17:26:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.38.141 - - [27/Feb/2008:17:27:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +134.173.59.157 - - [27/Feb/2008:17:28:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1858 +134.173.59.157 - - [27/Feb/2008:17:28:35 -0600] "GET /cgi-bin/wiki.pl?InstallationProblems HTTP/1.1" 200 4209 +24.10.16.193 - - [27/Feb/2008:17:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.59.157 - - [27/Feb/2008:17:29:44 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 971 +134.173.59.157 - - [27/Feb/2008:17:29:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +199.111.224.90 - - [27/Feb/2008:17:30:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:17:31:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +76.160.246.247 - - [27/Feb/2008:17:34:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:35:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:17:38:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [27/Feb/2008:17:40:04 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [27/Feb/2008:17:40:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.67.242.79 - - [27/Feb/2008:17:42:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +213.67.242.79 - - [27/Feb/2008:17:42:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +148.87.1.171 - - [27/Feb/2008:17:44:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +148.87.1.171 - - [27/Feb/2008:17:44:25 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +212.90.208.194 - - [27/Feb/2008:17:44:33 -0600] "GET /cv.html HTTP/1.1" 200 31798 +24.10.16.193 - - [27/Feb/2008:17:47:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:17:47:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:25 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.224.135.187 - - [27/Feb/2008:17:53:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +89.224.135.187 - - [27/Feb/2008:17:53:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +81.103.63.40 - - [27/Feb/2008:17:54:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +68.42.70.206 - - [27/Feb/2008:17:56:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.42.70.206 - - [27/Feb/2008:17:56:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:18:00:28 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.140.232.220 - - [27/Feb/2008:18:00:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [27/Feb/2008:18:01:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +140.180.132.213 - - [27/Feb/2008:18:09:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.180.132.213 - - [27/Feb/2008:18:09:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.146.214.212 - - [27/Feb/2008:18:13:05 -0600] "GET /dynamic/ HTTP/1.1" 304 - +66.146.214.212 - - [27/Feb/2008:18:13:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +86.157.119.197 - - [27/Feb/2008:18:16:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.223 - - [27/Feb/2008:18:17:08 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 +67.195.58.174 - - [27/Feb/2008:18:23:38 -0600] "GET /ply/ HTTP/1.0" 304 - +140.180.132.213 - - [27/Feb/2008:18:24:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.69.160.150 - - [27/Feb/2008:18:25:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.69.160.150 - - [27/Feb/2008:18:25:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +24.69.160.150 - - [27/Feb/2008:18:26:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +24.69.160.150 - - [27/Feb/2008:18:27:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +24.69.160.150 - - [27/Feb/2008:18:27:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMissingHeaderFiles HTTP/1.1" 200 3193 +24.69.160.150 - - [27/Feb/2008:18:27:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +24.69.160.150 - - [27/Feb/2008:18:27:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDevelopmentMachines HTTP/1.1" 200 1781 +24.69.160.150 - - [27/Feb/2008:18:27:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +24.69.160.150 - - [27/Feb/2008:18:27:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +24.69.160.150 - - [27/Feb/2008:18:28:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +24.69.160.150 - - [27/Feb/2008:18:28:13 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +24.69.160.150 - - [27/Feb/2008:18:28:17 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 +24.69.160.150 - - [27/Feb/2008:18:28:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +24.69.160.150 - - [27/Feb/2008:18:28:37 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +24.69.160.150 - - [27/Feb/2008:18:28:42 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4769 +24.69.160.150 - - [27/Feb/2008:18:28:45 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +24.69.160.150 - - [27/Feb/2008:18:29:08 -0600] "GET /cgi-bin/wiki.pl?PrettyXmlSwig HTTP/1.1" 200 1495 +24.69.160.150 - - [27/Feb/2008:18:29:24 -0600] "GET /cgi-bin/wiki.pl?Unit_Tests_With_SWIG HTTP/1.1" 200 2103 +66.146.214.212 - - [27/Feb/2008:18:31:34 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +74.6.26.203 - - [27/Feb/2008:18:33:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 +67.195.58.158 - - [27/Feb/2008:18:35:27 -0600] "GET /ply/ply-1.3.1.tar.gz HTTP/1.0" 304 - +67.195.58.158 - - [27/Feb/2008:18:35:28 -0600] "GET /ply/README HTTP/1.0" 304 - +199.111.224.90 - - [27/Feb/2008:18:41:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:43:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:18:43:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.192.172.49 - - [27/Feb/2008:18:52:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.192.172.49 - - [27/Feb/2008:18:54:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.57.91.136 - - [27/Feb/2008:18:54:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +85.192.172.49 - - [27/Feb/2008:18:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.192.172.49 - - [27/Feb/2008:18:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [27/Feb/2008:18:55:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +71.57.91.136 - - [27/Feb/2008:18:55:18 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +71.57.91.136 - - [27/Feb/2008:18:55:38 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +85.192.172.49 - - [27/Feb/2008:18:55:45 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +85.192.172.49 - - [27/Feb/2008:18:55:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +207.47.10.211 - - [27/Feb/2008:18:59:09 -0600] "GET /python.html HTTP/1.1" 200 18870 +207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +207.47.10.211 - - [27/Feb/2008:18:59:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.31.145 - - [27/Feb/2008:18:59:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +199.111.224.90 - - [27/Feb/2008:19:01:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.110.86.80 - - [27/Feb/2008:19:04:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.110.86.80 - - [27/Feb/2008:19:04:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +211.110.86.80 - - [27/Feb/2008:19:04:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.127.118.125 - - [27/Feb/2008:19:09:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET / HTTP/1.0" 200 4447 +62.149.227.70 - - [27/Feb/2008:19:13:07 -0600] "GET /training.html HTTP/1.0" 200 6154 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /software.html HTTP/1.0" 200 3163 +62.149.227.70 - - [27/Feb/2008:19:13:08 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +62.149.227.70 - - [27/Feb/2008:19:13:09 -0600] "GET /about.html HTTP/1.0" 200 7890 +62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /python.html HTTP/1.0" 200 18870 +62.149.227.70 - - [27/Feb/2008:19:13:10 -0600] "GET /index.html HTTP/1.0" 200 4447 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 +62.149.227.70 - - [27/Feb/2008:19:13:11 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/README HTTP/1.0" 200 8605 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +62.149.227.70 - - [27/Feb/2008:19:13:12 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /cv.html HTTP/1.0" 200 31798 +62.149.227.70 - - [27/Feb/2008:19:13:13 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.0" 200 158 +62.149.227.70 - - [27/Feb/2008:19:13:14 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +69.91.149.168 - - [27/Feb/2008:19:14:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +69.91.149.168 - - [27/Feb/2008:19:14:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.205.204 - - [27/Feb/2008:19:15:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.91.149.168 - - [27/Feb/2008:19:15:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +199.111.205.204 - - [27/Feb/2008:19:16:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:19:21:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.108 - - [27/Feb/2008:19:22:37 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +67.195.34.114 - - [27/Feb/2008:19:23:02 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +24.127.118.125 - - [27/Feb/2008:19:23:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:24:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:29:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [27/Feb/2008:19:31:10 -0600] "GET /photos/u505/pages/IMG_1522.htm HTTP/1.0" 404 133 +67.195.34.111 - - [27/Feb/2008:19:31:15 -0600] "GET /ply HTTP/1.0" 301 230 +199.111.205.204 - - [27/Feb/2008:19:31:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.111 - - [27/Feb/2008:19:31:19 -0600] "GET /ply/ HTTP/1.0" 200 8018 +24.127.118.125 - - [27/Feb/2008:19:34:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:19:36:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.92.161.138 - - [27/Feb/2008:19:37:00 -0600] "GET /ply/README HTTP/1.1" 200 8605 +193.252.149.15 - - [27/Feb/2008:19:38:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [27/Feb/2008:19:38:03 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.1" 200 142210 +201.50.187.62 - - [27/Feb/2008:19:39:45 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.50.187.62 - - [27/Feb/2008:19:39:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.97 - - [27/Feb/2008:19:40:04 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [27/Feb/2008:19:40:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:19:42:02 -0600] "GET / HTTP/1.0" 200 4447 +24.127.118.125 - - [27/Feb/2008:19:42:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:43:58 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [27/Feb/2008:19:44:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:44:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [27/Feb/2008:19:44:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +199.111.224.90 - - [27/Feb/2008:19:44:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.224.90 - - [27/Feb/2008:19:44:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:44:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [27/Feb/2008:19:50:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +69.213.243.113 - - [27/Feb/2008:19:50:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.127.118.125 - - [27/Feb/2008:19:57:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.180.198.130 - - [27/Feb/2008:19:59:26 -0600] "HEAD /ply/index.html HTTP/1.0" 200 0 +199.111.205.204 - - [27/Feb/2008:20:04:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +200.124.35.196 - - [27/Feb/2008:20:04:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.124.35.196 - - [27/Feb/2008:20:05:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.14 - - [27/Feb/2008:20:05:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +32.145.0.255 - - [27/Feb/2008:20:06:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +32.145.0.255 - - [27/Feb/2008:20:06:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:07:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:09:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +66.29.115.55 - - [27/Feb/2008:20:14:47 -0600] "GET / HTTP/1.1" 200 4447 +65.55.235.161 - - [27/Feb/2008:20:22:00 -0600] "GET /robots.txt HTTP/1.0" 200 71 +122.208.5.10 - - [27/Feb/2008:20:22:31 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +122.208.5.10 - - [27/Feb/2008:20:22:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.208.5.10 - - [27/Feb/2008:20:22:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +122.208.5.10 - - [27/Feb/2008:20:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.38.140.124 - - [27/Feb/2008:20:23:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +68.38.140.124 - - [27/Feb/2008:20:23:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.25.105.176 - - [27/Feb/2008:20:24:40 -0600] "GET / HTTP/1.1" 200 4447 +72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +72.25.105.176 - - [27/Feb/2008:20:24:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:29:46 -0600] "GET /about.html HTTP/1.1" 200 7890 +65.55.232.14 - - [27/Feb/2008:20:32:11 -0600] "GET /training.html HTTP/1.1" 200 6154 +199.111.205.204 - - [27/Feb/2008:20:32:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.129 - - [27/Feb/2008:20:33:15 -0600] "GET / HTTP/1.0" 200 4447 +199.111.205.204 - - [27/Feb/2008:20:37:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:20:38:56 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +65.55.232.14 - - [27/Feb/2008:20:38:58 -0600] "GET /index.html HTTP/1.1" 200 4447 +65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /software.html HTTP/1.1" 200 3163 +65.55.232.14 - - [27/Feb/2008:20:38:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 +199.111.205.204 - - [27/Feb/2008:20:39:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.135.190.54 - - [27/Feb/2008:20:41:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +199.111.205.204 - - [27/Feb/2008:20:42:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:20:43:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.204.154 - - [27/Feb/2008:20:44:26 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +199.111.205.204 - - [27/Feb/2008:20:51:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:20:52:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.158 - - [27/Feb/2008:20:53:53 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +70.190.236.166 - - [27/Feb/2008:20:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.54.165.36 - - [27/Feb/2008:21:03:56 -0600] "GET /robots.txt HTTP/1.1" 200 71 +69.213.243.113 - - [27/Feb/2008:21:04:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +69.213.243.113 - - [27/Feb/2008:21:04:25 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +69.213.243.113 - - [27/Feb/2008:21:04:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +69.213.243.113 - - [27/Feb/2008:21:04:39 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +199.111.205.204 - - [27/Feb/2008:21:05:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.176.224.242 - - [27/Feb/2008:21:05:53 -0600] "GET /robots.txt HTTP/1.0" 200 71 +199.111.205.204 - - [27/Feb/2008:21:11:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/ HTTP/1.1" 200 8018 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +199.111.195.244 - - [27/Feb/2008:21:11:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.195.244 - - [27/Feb/2008:21:11:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply HTTP/1.1" 301 242 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +132.206.52.168 - - [27/Feb/2008:21:13:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +132.206.52.168 - - [27/Feb/2008:21:13:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.168 - - [27/Feb/2008:21:13:32 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [27/Feb/2008:21:13:45 -0600] "GET / HTTP/1.1" 200 4447 +132.206.52.168 - - [27/Feb/2008:21:13:53 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.1.247.118 - - [27/Feb/2008:21:13:57 -0600] "GET /dynamic/ HTTP/1.1" 304 - +38.98.120.84 - - [27/Feb/2008:21:14:20 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:21 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:22 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:14:25 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:27 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:28 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [27/Feb/2008:21:14:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:31 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:33 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:34 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [27/Feb/2008:21:14:35 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:36 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:14:39 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [27/Feb/2008:21:14:40 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [27/Feb/2008:21:14:41 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [27/Feb/2008:21:14:43 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:46 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:47 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:48 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:14:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +38.98.120.84 - - [27/Feb/2008:21:14:50 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:51 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:52 -0600] "GET /swill/software.html HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:21:14:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:14:53 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:14:55 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:14:56 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:14:57 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:14:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /diet.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:15:02 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [27/Feb/2008:21:15:03 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:04 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [27/Feb/2008:21:15:05 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:15:06 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:07 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:08 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:09 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:10 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:11 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:12 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:13 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [27/Feb/2008:21:15:14 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:15 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:16 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:17 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:19 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:20 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:21 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:23 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [27/Feb/2008:21:15:24 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:25 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:26 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:27 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:28 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:29 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [27/Feb/2008:21:15:30 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:32 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:33 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:34 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:36 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:37 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [27/Feb/2008:21:15:38 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:15:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [27/Feb/2008:21:15:43 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:44 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:15:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:15:46 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:47 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:15:48 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:15:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:15:50 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:51 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [27/Feb/2008:21:15:52 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:53 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [27/Feb/2008:21:15:54 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:15:55 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [27/Feb/2008:21:15:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 135478 +38.98.120.84 - - [27/Feb/2008:21:15:57 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:15:58 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:15:59 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:00 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [27/Feb/2008:21:16:01 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:16:02 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [27/Feb/2008:21:16:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:04 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [27/Feb/2008:21:16:05 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:06 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:07 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:08 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [27/Feb/2008:21:16:09 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:16:11 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [27/Feb/2008:21:16:12 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:13 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 +38.98.120.84 - - [27/Feb/2008:21:16:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [27/Feb/2008:21:16:15 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +38.98.120.84 - - [27/Feb/2008:21:16:16 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [27/Feb/2008:21:16:17 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:18 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:19 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [27/Feb/2008:21:16:20 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:21 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:22 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:23 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:16:24 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:16:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [27/Feb/2008:21:16:26 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [27/Feb/2008:21:16:28 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:29 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:33 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [27/Feb/2008:21:16:34 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 +38.98.120.84 - - [27/Feb/2008:21:16:35 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [27/Feb/2008:21:16:36 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [27/Feb/2008:21:16:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:38 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [27/Feb/2008:21:16:46 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [27/Feb/2008:21:16:47 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [27/Feb/2008:21:16:48 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [27/Feb/2008:21:16:49 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:50 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:51 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [27/Feb/2008:21:16:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [27/Feb/2008:21:16:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:54 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [27/Feb/2008:21:16:55 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 +38.98.120.84 - - [27/Feb/2008:21:16:56 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:57 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [27/Feb/2008:21:16:58 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:17:00 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [27/Feb/2008:21:17:29 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 119844 +199.111.205.204 - - [27/Feb/2008:21:20:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [27/Feb/2008:21:27:16 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +76.206.238.222 - - [27/Feb/2008:21:29:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /training.html HTTP/1.0" 200 6154 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /software.html HTTP/1.0" 200 3163 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /index.html HTTP/1.0" 200 4447 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /writing.html HTTP/1.0" 200 2871 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 +208.223.208.181 - - [27/Feb/2008:21:29:57 -0600] "GET /about.html HTTP/1.0" 200 7890 +217.172.44.82 - - [27/Feb/2008:21:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +65.55.232.14 - - [27/Feb/2008:21:30:38 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +72.87.11.163 - - [27/Feb/2008:21:31:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.87.11.163 - - [27/Feb/2008:21:31:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +72.87.11.163 - - [27/Feb/2008:21:31:40 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +65.55.232.14 - - [27/Feb/2008:21:37:45 -0600] "GET /ply/README HTTP/1.1" 304 - +76.206.238.222 - - [27/Feb/2008:21:38:09 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 79526 +76.206.238.222 - - [27/Feb/2008:21:38:30 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 206 135312 +128.135.139.146 - - [27/Feb/2008:21:40:21 -0600] "GET /dynamic/ HTTP/1.1" 304 - +65.55.232.14 - - [27/Feb/2008:21:41:42 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +24.10.16.193 - - [27/Feb/2008:21:42:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:42:07 -0600] "GET /cv.html HTTP/1.1" 200 31798 +65.55.232.14 - - [27/Feb/2008:21:42:09 -0600] "GET /diet.html HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:42:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:43:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +65.55.232.14 - - [27/Feb/2008:21:46:58 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +65.55.232.14 - - [27/Feb/2008:21:46:59 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +65.55.232.14 - - [27/Feb/2008:21:47:03 -0600] "GET /dynamic/01Introduction.pdf HTTP/1.1" 200 3110734 +24.10.16.193 - - [27/Feb/2008:21:47:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:47:21 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 +24.10.16.193 - - [27/Feb/2008:21:48:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.176.147.11 - - [27/Feb/2008:21:49:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.15 - - [27/Feb/2008:21:49:23 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.232.15 - - [27/Feb/2008:21:49:27 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 200 3246437 +24.10.16.193 - - [27/Feb/2008:21:51:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:21:52:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:53:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:21:54:05 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +24.10.16.193 - - [27/Feb/2008:21:55:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:21:56:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:00:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:01:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.194.239 - - [27/Feb/2008:22:02:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.111.194.239 - - [27/Feb/2008:22:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.194.239 - - [27/Feb/2008:22:02:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.54.165.36 - - [27/Feb/2008:22:06:27 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +65.55.232.14 - - [27/Feb/2008:22:07:12 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /publications.html HTTP/1.1" 200 7758 +65.55.232.14 - - [27/Feb/2008:22:07:15 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +65.55.232.14 - - [27/Feb/2008:22:07:22 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +65.55.232.14 - - [27/Feb/2008:22:07:24 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +24.10.16.193 - - [27/Feb/2008:22:09:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.232.14 - - [27/Feb/2008:22:09:50 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 200 514533 +24.10.16.193 - - [27/Feb/2008:22:09:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:13:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:14:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.141 - - [27/Feb/2008:22:14:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.57.245.11 - - [27/Feb/2008:22:17:13 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +65.57.245.11 - - [27/Feb/2008:22:17:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.57.245.11 - - [27/Feb/2008:22:19:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.57.245.11 - - [27/Feb/2008:22:19:19 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET / HTTP/1.1" 200 4447 +201.78.136.222 - - [27/Feb/2008:22:20:55 -0600] "GET /ply HTTP/1.1" 301 242 +201.78.136.222 - - [27/Feb/2008:22:21:38 -0600] "GET /about.html HTTP/1.1" 200 7890 +199.111.205.204 - - [27/Feb/2008:22:22:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:24:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.57.245.11 - - [27/Feb/2008:22:25:47 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +199.111.205.204 - - [27/Feb/2008:22:26:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:27:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:28:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +199.111.205.204 - - [27/Feb/2008:22:28:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [27/Feb/2008:22:30:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.154.223.249 - - [27/Feb/2008:22:33:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.10.16.193 - - [27/Feb/2008:22:34:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [27/Feb/2008:22:38:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [27/Feb/2008:22:38:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.170 - - [27/Feb/2008:22:41:47 -0600] "GET / HTTP/1.0" 304 - +67.195.58.186 - - [27/Feb/2008:22:43:05 -0600] "GET /about.html HTTP/1.0" 200 7890 +128.143.255.129 - - [27/Feb/2008:22:48:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.72.178.39 - - [27/Feb/2008:22:55:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:22:55:29 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +190.128.108.147 - - [27/Feb/2008:23:07:42 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +190.128.108.147 - - [27/Feb/2008:23:07:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +190.128.108.147 - - [27/Feb/2008:23:07:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +190.128.108.147 - - [27/Feb/2008:23:07:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/ HTTP/1.1" 200 8018 +81.210.132.13 - - [27/Feb/2008:23:09:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +81.210.132.13 - - [27/Feb/2008:23:09:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.72.178.39 - - [27/Feb/2008:23:23:41 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +75.72.178.39 - - [27/Feb/2008:23:23:50 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +75.72.178.39 - - [27/Feb/2008:23:23:54 -0600] "GET /ply/README HTTP/1.1" 200 8605 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:23:30:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.58.86.1 - - [27/Feb/2008:23:30:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +24.10.16.193 - - [27/Feb/2008:23:40:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.107.0.101 - - [27/Feb/2008:23:41:48 -0600] "GET /photos/u505/pages/IMG_1543.htm HTTP/1.1" 404 133 +65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.124 - - [27/Feb/2008:23:45:06 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE007.HTM HTTP/1.1" 304 - +65.55.208.123 - - [27/Feb/2008:23:45:22 -0600] "GET /robots.txt HTTP/1.1" 200 71 +199.111.194.239 - - [27/Feb/2008:23:46:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.29.115.55 - - [27/Feb/2008:23:48:49 -0600] "GET /about.html HTTP/1.1" 200 7890 +66.232.113.62 - - [27/Feb/2008:23:49:57 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +201.63.117.142 - - [27/Feb/2008:23:50:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +217.172.56.49 - - [27/Feb/2008:23:50:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +195.229.242.154 - - [27/Feb/2008:23:50:20 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +89.122.29.76 - - [27/Feb/2008:23:54:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.10.16.193 - - [27/Feb/2008:23:59:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.201 - - [28/Feb/2008:00:02:30 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +206.51.226.87 - - [28/Feb/2008:00:13:22 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +213.209.210.70 - - [28/Feb/2008:00:13:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +212.247.11.155 - - [28/Feb/2008:00:13:35 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +72.236.184.249 - - [28/Feb/2008:00:13:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [28/Feb/2008:00:13:47 -0600] "GET /photos/wind/pages/IMG_1282.htm HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:13:48 -0600] "GET /photos/wind/pages/IMG_1329.htm HTTP/1.1" 404 133 +213.145.165.82 - - [28/Feb/2008:00:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +220.233.181.234 - - [28/Feb/2008:00:20:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:34 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.233.181.234 - - [28/Feb/2008:00:20:35 -0600] "GET /favicon.gif HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:00:20:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:00:26:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET / HTTP/1.1" 200 4447 +67.186.98.20 - - [28/Feb/2008:00:29:30 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.186.98.20 - - [28/Feb/2008:00:29:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:00:29:41 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +67.186.98.20 - - [28/Feb/2008:00:29:48 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:00:31:59 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:37:47 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:00:37:48 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:51:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:51:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:52:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.157.22.122 - - [28/Feb/2008:00:52:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +195.3.254.138 - - [28/Feb/2008:00:52:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +195.3.254.138 - - [28/Feb/2008:00:52:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.3.254.138 - - [28/Feb/2008:00:53:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:00:55:37 -0600] "GET /robots.txt HTTP/1.0" 200 71 +195.3.254.138 - - [28/Feb/2008:00:57:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +192.54.144.229 - - [28/Feb/2008:00:57:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:57:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +67.186.98.20 - - [28/Feb/2008:00:58:03 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:00:58:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:00:58:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +67.186.98.20 - - [28/Feb/2008:00:58:32 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +217.196.43.134 - - [28/Feb/2008:01:05:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.89.249.77 - - [28/Feb/2008:01:09:28 -0600] "GET /cv.html HTTP/1.1" 200 31798 +84.89.249.77 - - [28/Feb/2008:01:09:37 -0600] "GET / HTTP/1.1" 200 4447 +84.89.249.77 - - [28/Feb/2008:01:09:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +84.89.249.77 - - [28/Feb/2008:01:12:22 -0600] "GET /software.html HTTP/1.1" 200 3163 +65.55.208.118 - - [28/Feb/2008:01:12:45 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 304 - +84.89.249.77 - - [28/Feb/2008:01:12:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 +84.89.249.77 - - [28/Feb/2008:01:12:56 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +84.89.249.77 - - [28/Feb/2008:01:13:21 -0600] "GET /about.html HTTP/1.1" 200 7890 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +84.89.249.77 - - [28/Feb/2008:01:13:22 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +84.89.249.77 - - [28/Feb/2008:01:13:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +72.163.216.217 - - [28/Feb/2008:01:17:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.112 - - [28/Feb/2008:01:18:35 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +65.55.104.13 - - [28/Feb/2008:01:23:00 -0600] "GET /robots.txt HTTP/1.1" 200 71 +67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +67.123.80.214 - - [28/Feb/2008:01:38:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.123.80.214 - - [28/Feb/2008:01:38:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.123.80.214 - - [28/Feb/2008:01:38:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +67.123.80.214 - - [28/Feb/2008:01:38:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +67.123.80.214 - - [28/Feb/2008:01:41:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SwigHack HTTP/1.1" 200 2283 +194.186.83.193 - - [28/Feb/2008:01:42:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 56145 +195.3.254.138 - - [28/Feb/2008:01:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +88.131.106.15 - - [28/Feb/2008:01:52:39 -0600] "GET /ply/README HTTP/1.0" 200 8605 +24.223.228.170 - - [28/Feb/2008:01:59:34 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.223.228.170 - - [28/Feb/2008:01:59:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +24.223.228.170 - - [28/Feb/2008:01:59:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.232.58 - - [28/Feb/2008:02:02:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.58.232.58 - - [28/Feb/2008:02:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:02:02:49 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +61.135.166.102 - - [28/Feb/2008:02:04:34 -0600] "GET / HTTP/1.1" 200 4447 +220.181.38.169 - - [28/Feb/2008:02:07:15 -0600] "GET / HTTP/1.1" 200 4447 +67.195.58.174 - - [28/Feb/2008:02:26:24 -0600] "GET /ply/ HTTP/1.0" 304 - +65.214.45.129 - - [28/Feb/2008:02:26:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 +123.240.74.156 - - [28/Feb/2008:02:30:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +123.240.74.156 - - [28/Feb/2008:02:30:57 -0600] "GET /ply/ HTTP/1.1" 200 8018 +123.240.74.156 - - [28/Feb/2008:02:30:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:26 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.150.2.20 - - [28/Feb/2008:02:36:32 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +62.161.167.222 - - [28/Feb/2008:02:41:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.161.167.222 - - [28/Feb/2008:02:41:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:41:56 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.161.167.222 - - [28/Feb/2008:02:42:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:49:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:02:49:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.223.228.170 - - [28/Feb/2008:02:50:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +217.10.60.85 - - [28/Feb/2008:02:50:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:02:50:19 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.136.165.22 - - [28/Feb/2008:02:53:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.136.165.22 - - [28/Feb/2008:02:53:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.136.165.22 - - [28/Feb/2008:02:53:43 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +62.161.167.222 - - [28/Feb/2008:03:01:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +209.9.228.166 - - [28/Feb/2008:03:06:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 +209.9.228.174 - - [28/Feb/2008:03:06:39 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +62.161.167.222 - - [28/Feb/2008:03:08:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [28/Feb/2008:03:08:49 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.28.223 - - [28/Feb/2008:03:08:49 -0600] "GET /python/python.html HTTP/1.0" 404 133 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET / HTTP/1.1" 200 4447 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:03:09:04 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +98.215.100.68 - - [28/Feb/2008:03:09:07 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +203.101.103.2 - - [28/Feb/2008:03:09:25 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.246.173.21 - - [28/Feb/2008:03:11:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.246.173.21 - - [28/Feb/2008:03:11:32 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +65.55.208.118 - - [28/Feb/2008:03:12:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.1" 403 257 +83.12.228.78 - - [28/Feb/2008:03:14:42 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.1" 200 0 +139.149.31.231 - - [28/Feb/2008:03:15:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +139.149.31.231 - - [28/Feb/2008:03:15:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.3.134.34 - - [28/Feb/2008:03:19:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +198.49.180.40 - - [28/Feb/2008:03:20:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:23 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:31 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12707 +218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.107.55.253 - - [28/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +218.107.55.253 - - [28/Feb/2008:03:21:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.92.181.114 - - [28/Feb/2008:03:23:02 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.92.181.114 - - [28/Feb/2008:03:23:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.92.181.114 - - [28/Feb/2008:03:23:07 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +62.161.167.222 - - [28/Feb/2008:03:27:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.202.49.172 - - [28/Feb/2008:03:30:36 -0600] "GET /robots.txt HTTP/1.1" 200 71 +218.107.55.253 - - [28/Feb/2008:03:30:38 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.202.49.172 - - [28/Feb/2008:03:31:12 -0600] "GET /ply HTTP/1.1" 301 242 +67.202.49.172 - - [28/Feb/2008:03:31:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +218.107.55.253 - - [28/Feb/2008:03:31:36 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +83.206.220.147 - - [28/Feb/2008:03:40:59 -0600] "HEAD /cgi-bin/wiki.pl HTTP/1.0" 200 0 +83.206.220.147 - - [28/Feb/2008:03:41:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +82.127.117.160 - - [28/Feb/2008:03:43:59 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +134.173.56.81 - - [28/Feb/2008:03:46:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.173.56.81 - - [28/Feb/2008:03:46:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.145.209.82 - - [28/Feb/2008:03:47:12 -0600] "GET /ply/ HTTP/1.0" 200 8018 +89.145.209.82 - - [28/Feb/2008:03:47:13 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +89.145.209.82 - - [28/Feb/2008:03:47:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +134.173.56.81 - - [28/Feb/2008:03:48:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.56.81 - - [28/Feb/2008:03:48:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:03:50:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.145.209.82 - - [28/Feb/2008:03:52:02 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.252.149.16 - - [28/Feb/2008:03:53:39 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [28/Feb/2008:03:53:45 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +66.232.113.194 - - [28/Feb/2008:03:54:54 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2755 +72.236.184.249 - - [28/Feb/2008:03:54:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.222.157.76 - - [28/Feb/2008:03:54:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply HTTP/1.1" 301 242 +86.15.171.18 - - [28/Feb/2008:03:55:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.25.206.10 - - [28/Feb/2008:03:57:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.138.83.15 - - [28/Feb/2008:04:06:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +137.138.83.15 - - [28/Feb/2008:04:06:25 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.117 - - [28/Feb/2008:04:06:46 -0600] "GET /photos/wind/pages/IMG_1258.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:04:06:59 -0600] "GET /photos/wind/pages/IMG_1330.htm HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:04:07:37 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.215.100.68 - - [28/Feb/2008:04:07:43 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +98.215.100.68 - - [28/Feb/2008:04:08:07 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +67.195.58.158 - - [28/Feb/2008:04:13:19 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +198.54.202.210 - - [28/Feb/2008:04:13:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +60.248.16.38 - - [28/Feb/2008:04:15:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.117.168.219 - - [28/Feb/2008:04:22:43 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +206.51.237.114 - - [28/Feb/2008:04:24:03 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2741 +189.42.79.133 - - [28/Feb/2008:04:24:07 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.228.123.66 - - [28/Feb/2008:04:24:11 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +89.145.209.82 - - [28/Feb/2008:04:24:33 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.121 - - [28/Feb/2008:04:26:53 -0600] "GET /photos/wind/pages/IMG_1274.htm HTTP/1.1" 404 133 +203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +203.199.144.195 - - [28/Feb/2008:04:31:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +82.119.242.94 - - [28/Feb/2008:04:37:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.165.112.85 - - [28/Feb/2008:04:42:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.165.112.85 - - [28/Feb/2008:04:42:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.165.112.85 - - [28/Feb/2008:04:42:54 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +82.119.242.94 - - [28/Feb/2008:04:44:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.177.81 - - [28/Feb/2008:04:50:25 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.81 - - [28/Feb/2008:04:50:27 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1035 +203.213.7.130 - - [28/Feb/2008:04:57:34 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +203.213.7.130 - - [28/Feb/2008:04:57:36 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.38.104 - - [28/Feb/2008:05:08:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.104 - - [28/Feb/2008:05:09:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.38.104 - - [28/Feb/2008:05:09:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +153.110.6.241 - - [28/Feb/2008:05:22:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +153.110.6.241 - - [28/Feb/2008:05:22:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +203.213.7.130 - - [28/Feb/2008:05:27:00 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.215.172.82 - - [28/Feb/2008:05:29:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +88.215.172.82 - - [28/Feb/2008:05:29:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +88.215.172.82 - - [28/Feb/2008:05:29:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +62.180.231.91 - - [28/Feb/2008:05:30:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +62.180.231.91 - - [28/Feb/2008:05:30:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +147.228.209.194 - - [28/Feb/2008:05:32:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +147.228.209.194 - - [28/Feb/2008:05:32:03 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 20114 +89.145.209.82 - - [28/Feb/2008:05:32:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.119 - - [28/Feb/2008:05:39:06 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +74.6.25.199 - - [28/Feb/2008:05:39:28 -0600] "GET /cv.html HTTP/1.0" 304 - +74.6.19.156 - - [28/Feb/2008:05:40:54 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /ply/README HTTP/1.1" 200 8605 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.12.146.250 - - [28/Feb/2008:05:44:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.12.146.250 - - [28/Feb/2008:05:44:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [28/Feb/2008:05:44:50 -0600] "GET /photos/u505/pages/IMG_1516.htm HTTP/1.1" 404 133 +203.213.7.130 - - [28/Feb/2008:05:51:12 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.208.117 - - [28/Feb/2008:05:56:18 -0600] "GET /photos/wind/pages/IMG_1262.htm HTTP/1.1" 404 133 +153.110.6.241 - - [28/Feb/2008:06:01:14 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +203.213.7.130 - - [28/Feb/2008:06:09:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +59.124.68.145 - - [28/Feb/2008:06:10:45 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +59.124.68.145 - - [28/Feb/2008:06:10:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +59.124.68.145 - - [28/Feb/2008:06:10:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.13.207.74 - - [28/Feb/2008:06:12:43 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +83.13.207.74 - - [28/Feb/2008:06:12:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +83.13.207.74 - - [28/Feb/2008:06:12:57 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:06:15:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:06:16:08 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:06:16:11 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:06:16:19 -0600] "GET /cgi-bin/wiki.pl?IgnoreDirective HTTP/1.1" 200 2253 +83.13.207.74 - - [28/Feb/2008:06:17:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:06:18:20 -0600] "GET /cgi-bin/wiki.pl?NewobjectDirective HTTP/1.1" 200 4258 +80.200.221.74 - - [28/Feb/2008:06:18:29 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 +80.200.221.74 - - [28/Feb/2008:06:18:51 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +80.200.221.74 - - [28/Feb/2008:06:19:00 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 +80.200.221.74 - - [28/Feb/2008:06:19:19 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigTypemaps HTTP/1.1" 200 1591 +80.200.221.74 - - [28/Feb/2008:06:19:23 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +80.200.221.74 - - [28/Feb/2008:06:20:49 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:06:21:03 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +62.180.231.91 - - [28/Feb/2008:06:21:24 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +80.200.221.74 - - [28/Feb/2008:06:21:28 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:06:21:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:06:21:37 -0600] "GET /cgi-bin/wiki.pl?NodefaultDirective HTTP/1.1" 200 1742 +80.200.221.74 - - [28/Feb/2008:06:21:43 -0600] "GET /cgi-bin/wiki.pl?MakedefaultDirective HTTP/1.1" 200 2961 +80.200.221.74 - - [28/Feb/2008:06:22:04 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 +80.200.221.74 - - [28/Feb/2008:06:22:22 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +80.200.221.74 - - [28/Feb/2008:06:23:06 -0600] "GET /cgi-bin/wiki.pl?ContractDirective HTTP/1.1" 200 1512 +80.200.221.74 - - [28/Feb/2008:06:23:12 -0600] "GET /cgi-bin/wiki.pl?ConstantDirective HTTP/1.1" 200 1372 +72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET /robots.txt HTTP/1.1" 200 71 +72.20.109.37 - - [28/Feb/2008:06:28:13 -0600] "GET / HTTP/1.1" 200 4447 +72.20.109.37 - - [28/Feb/2008:06:28:14 -0600] "GET /about.html HTTP/1.1" 200 7890 +213.95.25.246 - - [28/Feb/2008:06:30:06 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +213.95.25.246 - - [28/Feb/2008:06:30:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.95.25.246 - - [28/Feb/2008:06:30:26 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +213.95.25.246 - - [28/Feb/2008:06:30:29 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +213.95.25.246 - - [28/Feb/2008:06:30:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +213.95.25.246 - - [28/Feb/2008:06:30:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +213.95.25.246 - - [28/Feb/2008:06:30:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +213.95.25.246 - - [28/Feb/2008:06:31:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +71.126.89.51 - - [28/Feb/2008:06:31:19 -0600] "GET / HTTP/1.1" 200 4447 +88.191.19.81 - - [28/Feb/2008:06:31:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.247.217.34 - - [28/Feb/2008:06:32:07 -0600] "GET /robots.txt HTTP/1.1" 200 71 +61.247.217.34 - - [28/Feb/2008:06:32:08 -0600] "GET / HTTP/1.1" 200 4447 +213.95.25.246 - - [28/Feb/2008:06:32:16 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +213.95.25.246 - - [28/Feb/2008:06:33:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +213.95.25.246 - - [28/Feb/2008:06:34:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqNothingWorks HTTP/1.1" 200 2629 +203.213.7.130 - - [28/Feb/2008:06:35:40 -0600] "GET /ply/ HTTP/1.0" 304 - +64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /robots.txt HTTP/1.0" 200 71 +64.34.145.194 - - [28/Feb/2008:06:36:09 -0600] "GET /photos/u505/pages/IMG_1492.htm HTTP/1.0" 404 133 +128.143.136.157 - - [28/Feb/2008:06:36:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:06:36:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.104.43.5 - - [28/Feb/2008:06:38:58 -0600] "GET /robots.txt HTTP/1.0" 200 71 +84.104.43.5 - - [28/Feb/2008:06:39:00 -0600] "GET /python.html HTTP/1.0" 200 18870 +60.50.9.191 - - [28/Feb/2008:06:39:25 -0600] "GET /ply/ HTTP/1.1" 200 8018 +60.50.9.191 - - [28/Feb/2008:06:39:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:06:39:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.93.128.164 - - [28/Feb/2008:06:44:43 -0600] "GET / HTTP/1.0" 200 4447 +91.93.128.164 - - [28/Feb/2008:06:44:44 -0600] "GET /python.html HTTP/1.0" 200 18870 +91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +91.93.128.164 - - [28/Feb/2008:06:44:45 -0600] "GET /training.html HTTP/1.0" 200 6154 +91.93.128.164 - - [28/Feb/2008:06:44:46 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /software.html HTTP/1.0" 200 3163 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /writing.html HTTP/1.0" 200 2871 +91.93.128.164 - - [28/Feb/2008:06:44:47 -0600] "GET /index.html HTTP/1.0" 200 4447 +91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +91.93.128.164 - - [28/Feb/2008:06:44:48 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +91.93.128.164 - - [28/Feb/2008:06:44:50 -0600] "GET /dynamic/assign5.html HTTP/1.0" 200 11008 +91.93.128.164 - - [28/Feb/2008:06:44:51 -0600] "GET /dynamic/assign4.html HTTP/1.0" 200 8712 +91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +91.93.128.164 - - [28/Feb/2008:06:44:52 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/soln12_1.html HTTP/1.0" 404 133 +91.93.128.164 - - [28/Feb/2008:06:44:53 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.0" 200 293 +91.93.128.164 - - [28/Feb/2008:06:44:54 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +129.192.97.6 - - [28/Feb/2008:06:47:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.0" 200 1352 +129.192.97.6 - - [28/Feb/2008:06:47:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 200 1620 +129.192.97.6 - - [28/Feb/2008:06:48:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.0" 200 990 +129.192.97.6 - - [28/Feb/2008:06:48:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE005.HTM HTTP/1.0" 200 1429 +129.192.97.6 - - [28/Feb/2008:06:48:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE006.HTM HTTP/1.0" 200 1254 +129.192.97.6 - - [28/Feb/2008:06:48:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.0" 200 1337 +129.192.97.6 - - [28/Feb/2008:06:48:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE008.HTM HTTP/1.0" 200 1231 +129.192.97.6 - - [28/Feb/2008:06:48:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE009.HTM HTTP/1.0" 200 1279 +129.192.97.6 - - [28/Feb/2008:06:48:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE010.HTM HTTP/1.0" 200 1403 +129.192.97.6 - - [28/Feb/2008:06:48:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:48:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE012.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:48:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE013.HTM HTTP/1.0" 200 1447 +129.192.97.6 - - [28/Feb/2008:06:48:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE014.HTM HTTP/1.0" 200 1232 +129.192.97.6 - - [28/Feb/2008:06:48:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE015.HTM HTTP/1.0" 200 1229 +129.192.97.6 - - [28/Feb/2008:06:48:44 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE016.HTM HTTP/1.0" 200 1343 +129.192.97.6 - - [28/Feb/2008:06:48:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE017.HTM HTTP/1.0" 200 1458 +129.192.97.6 - - [28/Feb/2008:06:48:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.0" 200 1429 +129.192.97.6 - - [28/Feb/2008:06:48:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE019.HTM HTTP/1.0" 200 1307 +129.192.97.6 - - [28/Feb/2008:06:48:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE020.HTM HTTP/1.0" 200 1338 +129.192.97.6 - - [28/Feb/2008:06:49:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE021.HTM HTTP/1.0" 200 1207 +129.192.97.6 - - [28/Feb/2008:06:49:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE022.HTM HTTP/1.0" 200 981 +129.192.97.6 - - [28/Feb/2008:06:49:14 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE023.HTM HTTP/1.0" 200 1576 +129.192.97.6 - - [28/Feb/2008:06:49:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE024.HTM HTTP/1.0" 200 1339 +129.192.97.6 - - [28/Feb/2008:06:49:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE025.HTM HTTP/1.0" 200 1570 +129.192.97.6 - - [28/Feb/2008:06:49:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE026.HTM HTTP/1.0" 200 1692 +129.192.97.6 - - [28/Feb/2008:06:49:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE027.HTM HTTP/1.0" 200 1334 +129.192.97.6 - - [28/Feb/2008:06:49:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE028.HTM HTTP/1.0" 200 1704 +129.192.97.6 - - [28/Feb/2008:06:49:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE029.HTM HTTP/1.0" 200 1491 +129.192.97.6 - - [28/Feb/2008:06:49:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE030.HTM HTTP/1.0" 200 1549 +129.192.97.6 - - [28/Feb/2008:06:49:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE031.HTM HTTP/1.0" 200 982 +129.192.97.6 - - [28/Feb/2008:06:49:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE032.HTM HTTP/1.0" 200 1444 +60.50.9.191 - - [28/Feb/2008:06:50:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.192.97.6 - - [28/Feb/2008:06:50:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE033.HTM HTTP/1.0" 200 1804 +60.50.9.191 - - [28/Feb/2008:06:50:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.192.97.6 - - [28/Feb/2008:06:50:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE034.HTM HTTP/1.0" 200 1586 +129.192.97.6 - - [28/Feb/2008:06:50:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.0" 200 1441 +129.192.97.6 - - [28/Feb/2008:06:51:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE036.HTM HTTP/1.0" 200 2010 +129.192.97.6 - - [28/Feb/2008:06:51:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE037.HTM HTTP/1.0" 200 1772 +129.192.97.6 - - [28/Feb/2008:06:51:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.0" 200 1373 +129.192.97.6 - - [28/Feb/2008:06:51:36 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE039.HTM HTTP/1.0" 200 1638 +129.192.97.6 - - [28/Feb/2008:06:51:39 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE040.HTM HTTP/1.0" 200 1910 +129.192.97.6 - - [28/Feb/2008:06:51:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE041.HTM HTTP/1.0" 200 2037 +129.192.97.6 - - [28/Feb/2008:06:51:46 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.0" 200 1336 +129.192.97.6 - - [28/Feb/2008:06:51:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE043.HTM HTTP/1.0" 200 1368 +129.192.97.6 - - [28/Feb/2008:06:51:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE044.HTM HTTP/1.0" 200 1000 +129.192.97.6 - - [28/Feb/2008:06:51:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 +129.192.97.6 - - [28/Feb/2008:06:51:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE046.HTM HTTP/1.0" 200 1738 +129.192.97.6 - - [28/Feb/2008:06:52:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE047.HTM HTTP/1.0" 200 1410 +129.192.97.6 - - [28/Feb/2008:06:52:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE048.HTM HTTP/1.0" 200 1654 +129.192.97.6 - - [28/Feb/2008:06:52:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE049.HTM HTTP/1.0" 200 1574 +129.192.97.6 - - [28/Feb/2008:06:52:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE050.HTM HTTP/1.0" 200 989 +129.192.97.6 - - [28/Feb/2008:06:52:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE051.HTM HTTP/1.0" 200 1476 +129.192.97.6 - - [28/Feb/2008:06:52:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1633 +129.192.97.6 - - [28/Feb/2008:06:52:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE053.HTM HTTP/1.0" 200 1661 +129.192.97.6 - - [28/Feb/2008:06:52:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE054.HTM HTTP/1.0" 200 1450 +129.192.97.6 - - [28/Feb/2008:06:52:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE055.HTM HTTP/1.0" 200 1319 +129.192.97.6 - - [28/Feb/2008:06:52:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE056.HTM HTTP/1.0" 200 1504 +129.192.97.6 - - [28/Feb/2008:06:52:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE057.HTM HTTP/1.0" 200 1847 +129.192.97.6 - - [28/Feb/2008:06:52:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.0" 200 1296 +213.95.25.246 - - [28/Feb/2008:06:52:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +129.192.97.6 - - [28/Feb/2008:06:52:31 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE059.HTM HTTP/1.0" 200 1694 +129.192.97.6 - - [28/Feb/2008:06:52:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE060.HTM HTTP/1.0" 200 1686 +129.192.97.6 - - [28/Feb/2008:06:52:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE061.HTM HTTP/1.0" 200 1508 +129.192.97.6 - - [28/Feb/2008:06:52:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.0" 200 1322 +129.192.97.6 - - [28/Feb/2008:06:52:40 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE063.HTM HTTP/1.0" 200 1511 +129.192.97.6 - - [28/Feb/2008:06:52:42 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE064.HTM HTTP/1.0" 200 971 +129.192.97.6 - - [28/Feb/2008:06:52:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE065.HTM HTTP/1.0" 200 1822 +129.192.97.6 - - [28/Feb/2008:06:52:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE066.HTM HTTP/1.0" 200 1538 +129.192.97.6 - - [28/Feb/2008:06:52:48 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE067.HTM HTTP/1.0" 200 1750 +129.192.97.6 - - [28/Feb/2008:06:52:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE068.HTM HTTP/1.0" 200 1394 +129.192.97.6 - - [28/Feb/2008:06:52:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE069.HTM HTTP/1.0" 200 1702 +129.192.97.6 - - [28/Feb/2008:06:52:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE070.HTM HTTP/1.0" 200 1698 +129.192.97.6 - - [28/Feb/2008:06:52:56 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE071.HTM HTTP/1.0" 200 1659 +129.192.97.6 - - [28/Feb/2008:06:52:57 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE072.HTM HTTP/1.0" 200 1850 +129.192.97.6 - - [28/Feb/2008:06:52:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE073.HTM HTTP/1.0" 200 1290 +129.192.97.6 - - [28/Feb/2008:06:53:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE074.HTM HTTP/1.0" 200 1351 +129.192.97.6 - - [28/Feb/2008:06:53:03 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE075.HTM HTTP/1.0" 200 1512 +129.192.97.6 - - [28/Feb/2008:06:53:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE076.HTM HTTP/1.0" 200 1650 +129.192.97.6 - - [28/Feb/2008:06:53:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE077.HTM HTTP/1.0" 200 1451 +129.192.97.6 - - [28/Feb/2008:06:53:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE078.HTM HTTP/1.0" 200 1329 +129.192.97.6 - - [28/Feb/2008:06:53:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE079.HTM HTTP/1.0" 200 1466 +129.192.97.6 - - [28/Feb/2008:06:53:11 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE080.HTM HTTP/1.0" 200 1578 +129.192.97.6 - - [28/Feb/2008:06:53:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE081.HTM HTTP/1.0" 200 1543 +129.192.97.6 - - [28/Feb/2008:06:53:18 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE082.HTM HTTP/1.0" 200 983 +129.192.97.6 - - [28/Feb/2008:06:53:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE083.HTM HTTP/1.0" 200 1418 +129.192.97.6 - - [28/Feb/2008:06:53:24 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE084.HTM HTTP/1.0" 200 1475 +129.192.97.6 - - [28/Feb/2008:06:53:30 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE085.HTM HTTP/1.0" 200 1739 +129.192.97.6 - - [28/Feb/2008:06:53:34 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE086.HTM HTTP/1.0" 200 1726 +129.192.97.6 - - [28/Feb/2008:06:53:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 +129.192.97.6 - - [28/Feb/2008:06:53:45 -0600] "GET /python/tutorial/beazley_advanced_python/ HTTP/1.0" 403 238 +213.95.25.246 - - [28/Feb/2008:06:53:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +129.192.97.6 - - [28/Feb/2008:06:53:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE088.HTM HTTP/1.0" 200 1723 +129.192.97.6 - - [28/Feb/2008:06:53:55 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE089.HTM HTTP/1.0" 200 1444 +129.192.97.6 - - [28/Feb/2008:06:53:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE090.HTM HTTP/1.0" 200 2001 +129.192.97.6 - - [28/Feb/2008:06:54:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE091.HTM HTTP/1.0" 200 1772 +129.192.97.6 - - [28/Feb/2008:06:54:02 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE092.HTM HTTP/1.0" 200 1427 +129.192.97.6 - - [28/Feb/2008:06:54:15 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE093.HTM HTTP/1.0" 200 1412 +129.192.97.6 - - [28/Feb/2008:06:54:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE094.HTM HTTP/1.0" 200 1435 +129.192.97.6 - - [28/Feb/2008:06:54:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 +129.192.97.6 - - [28/Feb/2008:06:54:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE096.HTM HTTP/1.0" 200 1671 +129.192.97.6 - - [28/Feb/2008:06:54:22 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE097.HTM HTTP/1.0" 200 1679 +129.192.97.6 - - [28/Feb/2008:06:54:23 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE098.HTM HTTP/1.0" 200 1328 +129.192.97.6 - - [28/Feb/2008:06:54:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE099.HTM HTTP/1.0" 200 1493 +129.192.97.6 - - [28/Feb/2008:06:54:26 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE100.HTM HTTP/1.0" 200 1562 +129.192.97.6 - - [28/Feb/2008:06:54:27 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE101.HTM HTTP/1.0" 200 980 +129.192.97.6 - - [28/Feb/2008:06:54:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE102.HTM HTTP/1.0" 200 1252 +129.192.97.6 - - [28/Feb/2008:06:54:43 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE103.HTM HTTP/1.0" 200 1330 +129.192.97.6 - - [28/Feb/2008:06:54:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE104.HTM HTTP/1.0" 200 1140 +129.192.97.6 - - [28/Feb/2008:06:54:52 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE105.HTM HTTP/1.0" 200 1256 +129.192.97.6 - - [28/Feb/2008:06:54:54 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 +129.192.97.6 - - [28/Feb/2008:06:54:58 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE107.HTM HTTP/1.0" 200 1397 +129.192.97.6 - - [28/Feb/2008:06:55:00 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE108.HTM HTTP/1.0" 200 1686 +129.192.97.6 - - [28/Feb/2008:06:55:01 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE109.HTM HTTP/1.0" 200 1858 +129.192.97.6 - - [28/Feb/2008:06:55:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE110.HTM HTTP/1.0" 200 1791 +129.192.97.6 - - [28/Feb/2008:06:55:07 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE111.HTM HTTP/1.0" 200 1431 +129.192.97.6 - - [28/Feb/2008:06:55:09 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE112.HTM HTTP/1.0" 200 987 +129.192.97.6 - - [28/Feb/2008:06:55:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE113.HTM HTTP/1.0" 200 1134 +129.192.97.6 - - [28/Feb/2008:06:55:12 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE114.HTM HTTP/1.0" 200 1821 +129.192.97.6 - - [28/Feb/2008:06:55:16 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 +129.192.97.6 - - [28/Feb/2008:06:55:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE116.HTM HTTP/1.0" 200 1585 +129.192.97.6 - - [28/Feb/2008:06:55:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE117.HTM HTTP/1.0" 200 979 +129.192.97.6 - - [28/Feb/2008:06:55:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE118.HTM HTTP/1.0" 200 1437 +38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [28/Feb/2008:06:55:25 -0600] "GET / HTTP/1.1" 200 4447 +129.192.97.6 - - [28/Feb/2008:06:55:38 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE119.HTM HTTP/1.0" 200 1144 +129.192.97.6 - - [28/Feb/2008:06:55:45 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE120.HTM HTTP/1.0" 200 1308 +129.192.97.6 - - [28/Feb/2008:06:55:59 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE121.HTM HTTP/1.0" 200 1316 +129.192.97.6 - - [28/Feb/2008:06:56:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE122.HTM HTTP/1.0" 200 1297 +129.192.97.6 - - [28/Feb/2008:06:56:13 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE123.HTM HTTP/1.0" 200 1195 +129.192.97.6 - - [28/Feb/2008:06:56:17 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE124.HTM HTTP/1.0" 200 1355 +129.192.97.6 - - [28/Feb/2008:06:56:19 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE125.HTM HTTP/1.0" 200 978 +129.192.97.6 - - [28/Feb/2008:06:56:21 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE126.HTM HTTP/1.0" 200 1555 +129.192.97.6 - - [28/Feb/2008:06:56:25 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE127.HTM HTTP/1.0" 404 133 +129.192.97.6 - - [28/Feb/2008:06:56:32 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/ HTTP/1.0" 403 245 +38.98.120.84 - - [28/Feb/2008:06:57:28 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:57:29 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:57:30 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:31 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:32 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:33 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:57:35 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:57:37 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:38 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:57:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:57:40 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +192.88.162.35 - - [28/Feb/2008:06:57:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +38.98.120.84 - - [28/Feb/2008:06:57:42 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:57:44 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:06:57:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:57:46 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:57:47 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:48 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +129.192.97.6 - - [28/Feb/2008:06:57:49 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE001.HTM HTTP/1.0" 200 1225 +38.98.120.84 - - [28/Feb/2008:06:57:50 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:57:51 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:06:58:02 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:06:58:04 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:06:58:05 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:06:58:06 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:07 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:08 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:09 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:58:10 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:11 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:58:12 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:13 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:16 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:17 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:18 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:19 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:20 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:21 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:22 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:23 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:25 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:26 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:06:58:27 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:28 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [28/Feb/2008:06:58:29 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:31 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:06:58:32 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:33 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:34 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:06:58:37 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:58:39 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:58:40 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:06:58:41 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:42 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:06:58:43 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:44 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:58:45 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:46 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:06:58:47 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:48 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /dynamic/dowportfolio2.rec HTTP/1.1" 200 399 +38.98.120.84 - - [28/Feb/2008:06:58:49 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:58:51 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:53 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:58:54 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:58:55 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:56 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:58:57 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:58:58 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:06:59:00 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:01 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:06:59:02 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:59:03 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:04 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:06:59:06 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:07 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:08 -0600] "GET /dynamic/dowportfolio2.csv HTTP/1.1" 200 293 +38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:12 -0600] "GET /swill/writing.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:13 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:14 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:15 -0600] "GET /swill/training.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:16 -0600] "GET /swill/about.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:17 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:18 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:19 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:06:59:21 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:06:59:22 -0600] "GET /dynamic/dowportfolio.csv HTTP/1.1" 200 158 +38.98.120.84 - - [28/Feb/2008:06:59:23 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:25 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:26 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:27 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:28 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:29 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:06:59:30 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:31 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:32 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:33 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:06:59:34 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:06:59:36 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:37 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:38 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:06:59:39 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:06:59:40 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:41 -0600] "GET /swill/consulting.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:42 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:06:59:43 -0600] "GET /diet.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:06:59:45 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:46 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:06:59:47 -0600] "GET /dynamic/dowportfolio.rec HTTP/1.1" 200 375 +38.98.120.84 - - [28/Feb/2008:06:59:48 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:06:59:52 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +38.98.120.84 - - [28/Feb/2008:06:59:53 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:54 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:06:59:55 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:06:59:56 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:06:59:57 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [28/Feb/2008:06:59:58 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:06:59:59 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:00:00 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:00:01 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:00:02 -0600] "GET /swill/software.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:00:03 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:04 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:00:05 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:06 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:00:07 -0600] "GET /dynamic/portfolio.txt HTTP/1.1" 200 100 +38.98.120.84 - - [28/Feb/2008:07:00:08 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:00:09 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:10 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:00:23 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 122740 +86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +86.197.225.66 - - [28/Feb/2008:07:00:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:00:56 -0600] "GET /papers/Perl98/swigperl.ps HTTP/1.1" 200 112604 +38.98.120.84 - - [28/Feb/2008:07:01:16 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:17 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:18 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:01:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:01:20 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:01:21 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:01:22 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:01:23 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:01:24 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:01:25 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:01:26 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:01:27 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:30 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [28/Feb/2008:07:01:31 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:32 -0600] "GET /about HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:07:01:34 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +38.98.120.84 - - [28/Feb/2008:07:01:35 -0600] "GET /assign5.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:01:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:01:37 -0600] "GET /dynamic/assign5 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:38 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:39 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:40 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:41 -0600] "GET /Doc/index.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:42 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc HTTP/1.1" 301 248 +38.98.120.84 - - [28/Feb/2008:07:01:43 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:44 -0600] "GET /swill/Doc/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:01:45 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:46 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc HTTP/1.1" 301 248 +38.98.120.84 - - [28/Feb/2008:07:01:47 -0600] "GET /swill/Doc/ HTTP/1.1" 200 39052 +38.98.120.84 - - [28/Feb/2008:07:01:48 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:49 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:50 -0600] "GET /swill/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:01:51 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:01:52 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:53 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:54 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:55 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:56 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:01:57 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:07:01:58 -0600] "GET /dynamic/sd.html HTTP/1.1" 200 1873 +38.98.120.84 - - [28/Feb/2008:07:01:59 -0600] "GET /dynamic/sd HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:01 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:02 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +38.98.120.84 - - [28/Feb/2008:07:02:03 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +65.55.208.118 - - [28/Feb/2008:07:02:04 -0600] "GET /photos/u505/pages/IMG_1510.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:04 -0600] "GET /dynamic/smackdown HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:06 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:07 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:07:02:08 -0600] "GET /papers/Py97/beazley.html HTTP/1.1" 200 31315 +38.98.120.84 - - [28/Feb/2008:07:02:09 -0600] "GET /papers/Py97/beazley HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:10 -0600] "GET /Py97/beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:11 -0600] "GET /papers/beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97 HTTP/1.1" 301 250 +38.98.120.84 - - [28/Feb/2008:07:02:12 -0600] "GET /papers/Py97/ HTTP/1.1" 403 222 +38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:02:13 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:02:14 -0600] "GET /beazley.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:15 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:02:16 -0600] "GET /training.html HTTP/1.1" 200 6154 +38.98.120.84 - - [28/Feb/2008:07:02:17 -0600] "GET /training HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:18 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:07:02:19 -0600] "GET /papers/Perl98/swigperl.htm HTTP/1.1" 200 73867 +38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:02:20 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:02:21 -0600] "GET /papers/Perl98/swigperl HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:22 -0600] "GET /papers/swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98 HTTP/1.1" 301 252 +38.98.120.84 - - [28/Feb/2008:07:02:23 -0600] "GET /papers/Perl98/ HTTP/1.1" 403 224 +38.98.120.84 - - [28/Feb/2008:07:02:24 -0600] "GET /swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:25 -0600] "GET /Perl98/swigperl.htm HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:28 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:29 -0600] "GET /dynamic/index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:30 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:31 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:07:02:32 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:02:37 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:02:38 -0600] "GET /dynamic/assign3 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:39 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:40 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:41 -0600] "GET /index.html HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:42 -0600] "GET /index HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:43 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:02:44 -0600] "GET /software.html HTTP/1.1" 200 3163 +38.98.120.84 - - [28/Feb/2008:07:02:45 -0600] "GET /software HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:46 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:47 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:02:48 -0600] "GET /ply/README HTTP/1.1" 200 8605 +38.98.120.84 - - [28/Feb/2008:07:02:49 -0600] "GET /README HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:02:50 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:52 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:53 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:07:02:54 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:02:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:02:56 -0600] "GET /ply/support HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:02:57 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:02:58 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:07:02:59 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:00 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:01 -0600] "GET /dynamic/assign4 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:02 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:03 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:03:04 -0600] "GET /writing.html HTTP/1.1" 200 2871 +38.98.120.84 - - [28/Feb/2008:07:03:05 -0600] "GET /writing HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:06 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:07:03:07 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:08 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:09 -0600] "GET /dynamic/syllabus HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:10 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:11 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:03:12 -0600] "GET /papers/Python2001/python.html HTTP/1.1" 200 38356 +38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:13 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:14 -0600] "GET /papers/python.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001 HTTP/1.1" 301 256 +38.98.120.84 - - [28/Feb/2008:07:03:15 -0600] "GET /papers/Python2001/ HTTP/1.1" 403 228 +38.98.120.84 - - [28/Feb/2008:07:03:16 -0600] "GET /papers/Python2001/python HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:17 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:03:18 -0600] "GET /Python2001/python.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:19 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:07:03:20 -0600] "GET /papers/Tcl96/tcl96.html HTTP/1.1" 200 39617 +38.98.120.84 - - [28/Feb/2008:07:03:21 -0600] "GET /papers/tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:22 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96 HTTP/1.1" 301 251 +38.98.120.84 - - [28/Feb/2008:07:03:23 -0600] "GET /papers/Tcl96/ HTTP/1.1" 403 223 +38.98.120.84 - - [28/Feb/2008:07:03:24 -0600] "GET /papers/Tcl96/tcl96 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:29 -0600] "GET /tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:30 -0600] "GET /Tcl96/tcl96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:31 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:07:03:32 -0600] "GET /per_secrets.html HTTP/1.1" 200 7958 +38.98.120.84 - - [28/Feb/2008:07:03:33 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:34 -0600] "GET /per HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:35 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +38.98.120.84 - - [28/Feb/2008:07:03:38 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:39 -0600] "GET /sysop HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:40 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:07:03:41 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:03:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:03:43 -0600] "GET /dynamic/assign2 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:44 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:03:45 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:03:46 -0600] "GET /papers/Py96/python96.html HTTP/1.1" 200 22442 +38.98.120.84 - - [28/Feb/2008:07:03:47 -0600] "GET /papers/python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:03:48 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96 HTTP/1.1" 301 250 +38.98.120.84 - - [28/Feb/2008:07:03:49 -0600] "GET /papers/Py96/ HTTP/1.1" 403 222 +38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /papers/Py96/python96 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:51 -0600] "GET /python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:03:52 -0600] "GET /Py96/python96.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:04 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:07:04:05 -0600] "GET /dynamic/assign1.html HTTP/1.1" 200 3047 +38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:06 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:07 -0600] "GET /dynamic/assign1 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:08 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:09 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:07:04:10 -0600] "GET /dynamic/soln1.html HTTP/1.1" 200 6197 +38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:11 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:26 -0600] "GET /dynamic/soln1 HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:27 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:28 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:07:04:35 -0600] "GET /dynamic/dowstocks.csv HTTP/1.1" 200 589814 +38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic HTTP/1.1" 301 246 +38.98.120.84 - - [28/Feb/2008:07:04:48 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +38.98.120.84 - - [28/Feb/2008:07:04:49 -0600] "GET /dynamic/dowstocks HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:50 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:04:51 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:52 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:04:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:04:56 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:04:57 -0600] "GET /papers/Tcl98/TclChap.html HTTP/1.1" 200 85901 +38.98.120.84 - - [28/Feb/2008:07:04:58 -0600] "GET /papers/TclChap.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers HTTP/1.1" 301 245 +38.98.120.84 - - [28/Feb/2008:07:04:59 -0600] "GET /papers/ HTTP/1.1" 403 217 +38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98 HTTP/1.1" 301 251 +38.98.120.84 - - [28/Feb/2008:07:05:00 -0600] "GET /papers/Tcl98/ HTTP/1.1" 403 223 +38.98.120.84 - - [28/Feb/2008:07:05:01 -0600] "GET /papers/Tcl98/TclChap HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:02 -0600] "GET /TclChap.html HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:07:05:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:11 -0600] "GET /Tcl98/TclChap.html HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:13 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET /cv.html HTTP/1.1" 200 31798 +38.98.120.84 - - [28/Feb/2008:07:05:14 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:24 -0600] "GET /cv HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:26 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/ HTTP/1.0" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:27 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +200.19.92.58 - - [28/Feb/2008:07:05:27 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +200.19.92.58 - - [28/Feb/2008:07:05:28 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill HTTP/1.1" 301 244 +38.98.120.84 - - [28/Feb/2008:07:05:28 -0600] "GET /swill/ HTTP/1.1" 200 3786 +38.98.120.84 - - [28/Feb/2008:07:05:29 -0600] "GET /swill/exec HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:30 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:07:05:32 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:05:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:34 -0600] "GET /ply/ply HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:35 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:05:36 -0600] "GET /publications.html HTTP/1.1" 200 7758 +38.98.120.84 - - [28/Feb/2008:07:05:37 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:38 -0600] "GET /publications HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:39 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:05:40 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +38.98.120.84 - - [28/Feb/2008:07:05:41 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [28/Feb/2008:07:05:42 -0600] "GET /consulting HTTP/1.1" 404 133 +38.98.120.84 - - [28/Feb/2008:07:05:43 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:05:44 -0600] "GET /python.html HTTP/1.1" 200 18870 +38.98.120.84 - - [28/Feb/2008:07:05:47 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply HTTP/1.1" 301 242 +38.98.120.84 - - [28/Feb/2008:07:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +38.98.120.84 - - [28/Feb/2008:07:05:52 -0600] "GET /ply/example HTTP/1.1" 404 133 +74.6.25.234 - - [28/Feb/2008:07:06:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE003.HTM HTTP/1.0" 304 - +200.19.92.58 - - [28/Feb/2008:07:09:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +62.161.167.222 - - [28/Feb/2008:07:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET / HTTP/1.1" 200 4447 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.252.229.225 - - [28/Feb/2008:07:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.252.229.225 - - [28/Feb/2008:07:09:26 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +68.252.229.225 - - [28/Feb/2008:07:09:28 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +65.55.208.118 - - [28/Feb/2008:07:09:38 -0600] "GET /photos/u505/pages/IMG_1538.htm HTTP/1.1" 404 133 +74.6.24.10 - - [28/Feb/2008:07:11:23 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.0" 304 - +65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1278.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:13:44 -0600] "GET /photos/wind/pages/IMG_1256.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:13:45 -0600] "GET /photos/wind/pages/IMG_1304.htm HTTP/1.1" 404 133 +65.55.208.117 - - [28/Feb/2008:07:14:41 -0600] "GET /photos/wind/pages/IMG_1295.htm HTTP/1.1" 404 133 +193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.174.238.115 - - [28/Feb/2008:07:14:52 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.174.238.115 - - [28/Feb/2008:07:14:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:03 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.41.248 - - [28/Feb/2008:07:17:08 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +65.214.45.129 - - [28/Feb/2008:07:17:23 -0600] "GET /python/per_secrets.html HTTP/1.0" 404 133 +60.50.9.191 - - [28/Feb/2008:07:19:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +60.50.9.191 - - [28/Feb/2008:07:21:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:07:24:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.161.167.222 - - [28/Feb/2008:07:24:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.161.167.222 - - [28/Feb/2008:07:24:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:07:24:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.127.117.160 - - [28/Feb/2008:07:24:42 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +59.96.12.231 - - [28/Feb/2008:07:26:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +59.96.12.231 - - [28/Feb/2008:07:26:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.127.117.160 - - [28/Feb/2008:07:27:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +82.127.117.160 - - [28/Feb/2008:07:28:11 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.10.60.85 - - [28/Feb/2008:07:30:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:07:30:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +217.10.60.85 - - [28/Feb/2008:07:30:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +217.10.60.85 - - [28/Feb/2008:07:31:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.10.60.85 - - [28/Feb/2008:07:31:11 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +217.10.60.85 - - [28/Feb/2008:07:31:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +217.10.60.85 - - [28/Feb/2008:07:31:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +217.10.60.85 - - [28/Feb/2008:07:31:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +60.50.9.191 - - [28/Feb/2008:07:32:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [28/Feb/2008:07:32:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Tcl HTTP/1.1" 200 1399 +217.10.60.85 - - [28/Feb/2008:07:32:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +217.10.60.85 - - [28/Feb/2008:07:32:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +65.214.45.129 - - [28/Feb/2008:07:41:04 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE045.HTM HTTP/1.0" 200 1333 +68.252.229.225 - - [28/Feb/2008:07:44:31 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.141.224.186 - - [28/Feb/2008:07:46:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.141.224.186 - - [28/Feb/2008:07:46:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.141.224.186 - - [28/Feb/2008:07:47:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +83.170.97.200 - - [28/Feb/2008:07:47:35 -0600] "GET / HTTP/1.0" 200 4447 +83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +83.170.97.200 - - [28/Feb/2008:07:47:36 -0600] "GET /training.html HTTP/1.0" 200 6154 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /python.html HTTP/1.0" 200 18870 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5313 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /index.html HTTP/1.0" 200 4447 +83.170.97.200 - - [28/Feb/2008:07:47:37 -0600] "GET /writing.html HTTP/1.0" 200 2871 +83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /about.html HTTP/1.0" 200 7890 +83.170.97.200 - - [28/Feb/2008:07:47:38 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +83.170.97.200 - - [28/Feb/2008:07:47:39 -0600] "GET /dynamic/smackdown.py HTTP/1.0" 200 1981 +83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +83.170.97.200 - - [28/Feb/2008:07:47:40 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /cv.html HTTP/1.0" 200 31798 +83.170.97.200 - - [28/Feb/2008:07:47:41 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 +82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.95.165.247 - - [28/Feb/2008:07:48:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.95.165.247 - - [28/Feb/2008:07:48:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.95.165.247 - - [28/Feb/2008:07:49:08 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +201.236.226.90 - - [28/Feb/2008:07:51:01 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +82.95.165.247 - - [28/Feb/2008:07:53:49 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.200.221.74 - - [28/Feb/2008:07:57:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.200.221.74 - - [28/Feb/2008:07:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.200.221.74 - - [28/Feb/2008:07:57:36 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +80.200.221.74 - - [28/Feb/2008:07:57:43 -0600] "GET /cgi-bin/wiki.pl?RenameDirective HTTP/1.1" 200 3672 +83.12.146.250 - - [28/Feb/2008:08:00:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:08:05:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.79.171.46 - - [28/Feb/2008:08:07:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ModuleDirective HTTP/1.1" 200 2302 +222.221.6.144 - - [28/Feb/2008:08:07:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +24.1.159.241 - - [28/Feb/2008:08:08:05 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +24.1.159.241 - - [28/Feb/2008:08:08:20 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 279116 +80.200.221.74 - - [28/Feb/2008:08:09:20 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +69.147.83.113 - - [28/Feb/2008:08:10:34 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +194.209.8.145 - - [28/Feb/2008:08:11:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.209.8.145 - - [28/Feb/2008:08:11:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +194.209.8.145 - - [28/Feb/2008:08:11:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +219.136.230.60 - - [28/Feb/2008:08:14:15 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 - +220.150.154.145 - - [28/Feb/2008:08:14:20 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +220.150.154.145 - - [28/Feb/2008:08:14:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:08:16:22 -0600] "GET /cgi-bin/wiki.pl?DefineDirective HTTP/1.1" 200 2760 +80.200.221.74 - - [28/Feb/2008:08:16:39 -0600] "GET /cgi-bin/wiki.pl?FeatureDirective HTTP/1.1" 200 4535 +208.51.93.163 - - [28/Feb/2008:08:16:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +208.51.93.163 - - [28/Feb/2008:08:16:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.200.221.74 - - [28/Feb/2008:08:16:53 -0600] "GET /cgi-bin/wiki.pl?NameDirective HTTP/1.1" 200 1785 +80.200.221.74 - - [28/Feb/2008:08:17:05 -0600] "GET /cgi-bin/wiki.pl?CodeInsertionDirective HTTP/1.1" 200 2920 +80.200.221.74 - - [28/Feb/2008:08:17:08 -0600] "GET /cgi-bin/wiki.pl?TypemapDirective HTTP/1.1" 200 1584 +65.55.212.77 - - [28/Feb/2008:08:19:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.19.101 - - [28/Feb/2008:08:20:10 -0600] "GET /swill/Doc/ HTTP/1.0" 304 - +82.127.117.160 - - [28/Feb/2008:08:21:46 -0600] "GET /ply HTTP/1.0" 301 230 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:27:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:27:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.119 - - [28/Feb/2008:08:28:10 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE087.HTM HTTP/1.0" 200 1575 +193.128.72.68 - - [28/Feb/2008:08:28:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.128.72.68 - - [28/Feb/2008:08:28:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:29:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +138.100.218.23 - - [28/Feb/2008:08:30:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +138.100.218.23 - - [28/Feb/2008:08:30:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.41.248 - - [28/Feb/2008:08:33:24 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +69.217.73.2 - - [28/Feb/2008:08:34:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +69.217.73.2 - - [28/Feb/2008:08:34:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.21.88.201 - - [28/Feb/2008:08:35:01 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.21.88.201 - - [28/Feb/2008:08:35:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.21.88.201 - - [28/Feb/2008:08:35:04 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +69.217.73.2 - - [28/Feb/2008:08:36:16 -0600] "GET / HTTP/1.1" 200 4447 +69.217.73.2 - - [28/Feb/2008:08:36:17 -0600] "GET /index.html HTTP/1.1" 200 4447 +69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /training.html HTTP/1.1" 200 6154 +69.217.73.2 - - [28/Feb/2008:08:36:18 -0600] "GET /software.html HTTP/1.1" 200 3163 +69.217.73.2 - - [28/Feb/2008:08:36:20 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +69.217.73.2 - - [28/Feb/2008:08:36:21 -0600] "GET /writing.html HTTP/1.1" 200 2871 +69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /about.html HTTP/1.1" 200 7890 +69.217.73.2 - - [28/Feb/2008:08:36:22 -0600] "GET /python.html HTTP/1.1" 200 18870 +69.217.73.2 - - [28/Feb/2008:08:36:24 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +69.217.73.2 - - [28/Feb/2008:08:36:25 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.229.38.64 - - [28/Feb/2008:08:44:48 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +80.229.38.64 - - [28/Feb/2008:08:44:52 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/SwigDirectives HTTP/1.1" 200 4512 +64.124.85.71 - - [28/Feb/2008:08:45:09 -0600] "GET /robots.txt HTTP/1.1" 200 71 +82.95.165.247 - - [28/Feb/2008:08:52:25 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +216.201.139.126 - - [28/Feb/2008:08:54:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +82.127.117.160 - - [28/Feb/2008:08:55:29 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/ HTTP/1.1" 200 8018 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +208.78.145.1 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:03:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.78.145.1 - - [28/Feb/2008:09:03:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +61.48.58.106 - - [28/Feb/2008:09:05:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:06:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.161.167.222 - - [28/Feb/2008:09:07:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.138.208.6 - - [28/Feb/2008:09:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.138.208.6 - - [28/Feb/2008:09:11:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.138.208.6 - - [28/Feb/2008:09:11:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [28/Feb/2008:09:14:22 -0600] "GET /ply/ HTTP/1.0" 304 - +128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.139.150 - - [28/Feb/2008:09:16:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.139.150 - - [28/Feb/2008:09:17:14 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +193.138.208.6 - - [28/Feb/2008:09:17:37 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/ HTTP/1.0" 200 8018 +193.0.96.15 - - [28/Feb/2008:09:20:41 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +193.0.96.15 - - [28/Feb/2008:09:20:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.0.96.15 - - [28/Feb/2008:09:21:26 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +193.0.96.15 - - [28/Feb/2008:09:21:37 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +128.221.197.20 - - [28/Feb/2008:09:25:33 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +150.210.155.167 - - [28/Feb/2008:09:25:36 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +62.161.167.222 - - [28/Feb/2008:09:26:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.21.20.162 - - [28/Feb/2008:09:27:17 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +65.55.208.119 - - [28/Feb/2008:09:28:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE038.HTM HTTP/1.1" 304 - +193.0.96.15 - - [28/Feb/2008:09:32:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +129.194.8.73 - - [28/Feb/2008:09:32:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.194.8.73 - - [28/Feb/2008:09:33:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:09:33:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +84.168.117.112 - - [28/Feb/2008:09:35:43 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +84.168.117.112 - - [28/Feb/2008:09:35:44 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /python.html HTTP/1.1" 200 18870 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.190.85.89 - - [28/Feb/2008:09:36:19 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.97.120.226 - - [28/Feb/2008:09:37:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.97.120.226 - - [28/Feb/2008:09:37:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.97.120.226 - - [28/Feb/2008:09:37:18 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.168.117.112 - - [28/Feb/2008:09:37:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +84.168.117.112 - - [28/Feb/2008:09:37:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.80.22.103 - - [28/Feb/2008:09:39:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +195.80.22.103 - - [28/Feb/2008:09:39:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +195.80.22.103 - - [28/Feb/2008:09:40:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +195.80.22.103 - - [28/Feb/2008:09:40:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +129.194.8.73 - - [28/Feb/2008:09:40:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +98.193.69.179 - - [28/Feb/2008:09:41:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:51 -0600] "GET / HTTP/1.1" 200 4447 +201.236.226.90 - - [28/Feb/2008:09:42:53 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:09:42:56 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +88.80.205.215 - - [28/Feb/2008:09:43:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +88.80.205.215 - - [28/Feb/2008:09:43:39 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +98.193.69.179 - - [28/Feb/2008:09:43:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:09:43:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +193.0.96.15 - - [28/Feb/2008:09:44:21 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +193.0.96.15 - - [28/Feb/2008:09:44:24 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +65.55.208.119 - - [28/Feb/2008:09:46:37 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE018.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:46:38 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE012.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:46:58 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE028.HTM HTTP/1.1" 304 - +65.55.208.119 - - [28/Feb/2008:09:47:00 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE119.HTM HTTP/1.1" 304 - +82.119.242.94 - - [28/Feb/2008:09:48:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +82.119.242.94 - - [28/Feb/2008:09:48:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:09:55:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.71.37.92 - - [28/Feb/2008:09:56:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +212.71.37.92 - - [28/Feb/2008:09:56:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +194.76.29.2 - - [28/Feb/2008:09:56:41 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.76.29.2 - - [28/Feb/2008:09:57:21 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.0" 200 1773 +79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /cv.html HTTP/1.1" 200 31798 +79.77.245.147 - - [28/Feb/2008:09:57:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +194.76.29.2 - - [28/Feb/2008:09:57:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +194.76.29.2 - - [28/Feb/2008:09:58:03 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.0" 200 2320 +194.76.29.2 - - [28/Feb/2008:09:58:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.76.29.2 - - [28/Feb/2008:09:58:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:09:58:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:09:58:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +194.76.29.2 - - [28/Feb/2008:10:00:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqSolarisSharedLibraries HTTP/1.0" 200 2040 +194.76.29.2 - - [28/Feb/2008:10:01:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqAIXSharedLibraries HTTP/1.0" 200 3494 +64.234.244.162 - - [28/Feb/2008:10:02:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.0.96.15 - - [28/Feb/2008:10:04:48 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +193.0.96.15 - - [28/Feb/2008:10:05:08 -0600] "GET /python.html HTTP/1.0" 200 18870 +193.0.96.15 - - [28/Feb/2008:10:05:09 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +64.236.139.6 - - [28/Feb/2008:10:07:44 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +64.236.139.6 - - [28/Feb/2008:10:07:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.236.139.6 - - [28/Feb/2008:10:07:47 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +64.236.139.6 - - [28/Feb/2008:10:07:55 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +193.0.96.15 - - [28/Feb/2008:10:07:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.236.139.6 - - [28/Feb/2008:10:07:59 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +128.221.197.20 - - [28/Feb/2008:10:08:51 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +64.236.139.6 - - [28/Feb/2008:10:09:29 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Autotools HTTP/1.1" 200 1653 +64.236.139.6 - - [28/Feb/2008:10:09:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +64.236.139.6 - - [28/Feb/2008:10:10:51 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Typemaps HTTP/1.1" 200 4084 +64.236.139.6 - - [28/Feb/2008:10:11:35 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ruby HTTP/1.1" 200 2050 +64.236.139.6 - - [28/Feb/2008:10:13:42 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +64.236.139.6 - - [28/Feb/2008:10:14:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +98.193.69.179 - - [28/Feb/2008:10:16:52 -0600] "GET /dynamic/ffcache.zip HTTP/1.1" 200 4919642 +89.189.65.147 - - [28/Feb/2008:10:17:37 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.189.65.147 - - [28/Feb/2008:10:18:09 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +132.168.9.102 - - [28/Feb/2008:10:23:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.168.9.102 - - [28/Feb/2008:10:23:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +132.168.9.102 - - [28/Feb/2008:10:23:28 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +132.168.9.102 - - [28/Feb/2008:10:23:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +194.76.29.2 - - [28/Feb/2008:10:24:14 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.0" 200 1589 +128.221.197.20 - - [28/Feb/2008:10:29:52 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +70.249.147.120 - - [28/Feb/2008:10:31:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.249.147.120 - - [28/Feb/2008:10:31:58 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.249.147.120 - - [28/Feb/2008:10:32:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [28/Feb/2008:10:33:45 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +128.221.197.20 - - [28/Feb/2008:10:33:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +81.52.143.15 - - [28/Feb/2008:10:34:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +81.52.143.15 - - [28/Feb/2008:10:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +193.158.76.56 - - [28/Feb/2008:10:36:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +193.158.76.56 - - [28/Feb/2008:10:36:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:36:25 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:36:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +193.158.76.56 - - [28/Feb/2008:10:36:30 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +193.158.76.56 - - [28/Feb/2008:10:36:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +193.158.76.56 - - [28/Feb/2008:10:36:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +193.158.76.56 - - [28/Feb/2008:10:36:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUndeclaredClass HTTP/1.1" 200 2352 +193.158.76.56 - - [28/Feb/2008:10:37:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.158.76.56 - - [28/Feb/2008:10:37:25 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 944 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.71.33.174 - - [28/Feb/2008:10:39:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.71.33.174 - - [28/Feb/2008:10:39:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +207.71.33.174 - - [28/Feb/2008:10:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +64.234.244.162 - - [28/Feb/2008:10:39:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:10:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:19 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +98.215.100.68 - - [28/Feb/2008:10:43:22 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +66.1.67.100 - - [28/Feb/2008:10:43:22 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.1.67.100 - - [28/Feb/2008:10:43:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.1.67.100 - - [28/Feb/2008:10:43:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.244.205.42 - - [28/Feb/2008:10:43:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +99.244.205.42 - - [28/Feb/2008:10:43:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:41 -0600] "GET /dynamic/assign4.html HTTP/1.1" 200 8712 +99.244.205.42 - - [28/Feb/2008:10:43:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.215.100.68 - - [28/Feb/2008:10:43:49 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +99.244.205.42 - - [28/Feb/2008:10:43:54 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +208.36.144.7 - - [28/Feb/2008:10:44:03 -0600] "GET /robots.txt HTTP/1.0" 200 71 +66.1.67.100 - - [28/Feb/2008:10:44:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [28/Feb/2008:10:45:04 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +148.233.159.58 - - [28/Feb/2008:10:45:05 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +66.1.67.100 - - [28/Feb/2008:10:45:45 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +99.244.205.42 - - [28/Feb/2008:10:46:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.149.16.155 - - [28/Feb/2008:10:49:56 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +80.149.16.155 - - [28/Feb/2008:10:50:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.149.16.155 - - [28/Feb/2008:10:50:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.212 - - [28/Feb/2008:10:50:06 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +69.46.29.140 - - [28/Feb/2008:10:50:51 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2732 +80.97.94.178 - - [28/Feb/2008:10:50:58 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.221.197.20 - - [28/Feb/2008:10:52:34 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +189.24.63.240 - - [28/Feb/2008:10:53:07 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +189.24.63.240 - - [28/Feb/2008:10:53:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.221.197.20 - - [28/Feb/2008:10:53:20 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +65.166.139.21 - - [28/Feb/2008:10:55:07 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.166.139.21 - - [28/Feb/2008:10:55:25 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +65.166.139.21 - - [28/Feb/2008:10:55:32 -0600] "GET /cgi-bin/wiki.pl?DavidBeazley HTTP/1.1" 200 2019 +65.166.139.21 - - [28/Feb/2008:10:55:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +65.166.139.21 - - [28/Feb/2008:10:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +65.166.139.21 - - [28/Feb/2008:10:56:53 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +65.166.139.21 - - [28/Feb/2008:10:57:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqBuildErrorsRedHat HTTP/1.1" 200 2099 +65.166.139.21 - - [28/Feb/2008:10:57:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.1" 200 2861 +65.166.139.21 - - [28/Feb/2008:10:57:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCannotFindSwigDotSwg HTTP/1.1" 200 2455 +65.166.139.21 - - [28/Feb/2008:10:58:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaqInstallSwigInDifferentDirectory HTTP/1.1" 200 2271 +65.166.139.21 - - [28/Feb/2008:10:58:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Types HTTP/1.1" 200 3441 +65.166.139.21 - - [28/Feb/2008:10:58:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +64.234.244.162 - - [28/Feb/2008:11:02:45 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.54.202.234 - - [28/Feb/2008:11:05:53 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +196.25.255.210 - - [28/Feb/2008:11:05:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.246 - - [28/Feb/2008:11:05:57 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +196.25.255.210 - - [28/Feb/2008:11:06:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +196.25.255.210 - - [28/Feb/2008:11:07:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +196.25.255.210 - - [28/Feb/2008:11:07:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +198.54.202.218 - - [28/Feb/2008:11:07:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/MultipleLanguages HTTP/1.1" 200 2332 +196.25.255.194 - - [28/Feb/2008:11:08:06 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +196.25.255.250 - - [28/Feb/2008:11:08:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +196.25.255.210 - - [28/Feb/2008:11:08:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +212.56.88.112 - - [28/Feb/2008:11:11:14 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +24.7.210.64 - - [28/Feb/2008:11:11:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.221.197.20 - - [28/Feb/2008:11:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +24.7.210.64 - - [28/Feb/2008:11:11:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.120 - - [28/Feb/2008:11:12:53 -0600] "GET /sysop.html HTTP/1.1" 200 1760 +192.109.190.88 - - [28/Feb/2008:11:13:59 -0600] "GET /ply/PLYTalk.pdf HTTP/1.1" 200 194510 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.143.24.249 - - [28/Feb/2008:11:15:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.24.249 - - [28/Feb/2008:11:15:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:11:20:28 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE062.HTM HTTP/1.1" 304 - +208.36.144.7 - - [28/Feb/2008:11:21:55 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.139.146 - - [28/Feb/2008:11:30:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 304 - +65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.214.45.101 - - [28/Feb/2008:11:34:10 -0600] "GET /ply/ HTTP/1.0" 200 8018 +65.55.208.117 - - [28/Feb/2008:11:34:35 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE004.HTM HTTP/1.1" 304 - +198.253.24.5 - - [28/Feb/2008:11:34:54 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +198.253.24.5 - - [28/Feb/2008:11:34:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.253.24.5 - - [28/Feb/2008:11:35:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.1" 200 1636 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.31.57 - - [28/Feb/2008:11:37:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.223.31.57 - - [28/Feb/2008:11:37:21 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +64.234.244.162 - - [28/Feb/2008:11:40:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.77.240.100 - - [28/Feb/2008:11:43:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +151.77.240.100 - - [28/Feb/2008:11:43:23 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +151.77.240.100 - - [28/Feb/2008:11:43:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +52.128.30.11 - - [28/Feb/2008:11:46:24 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.195.58.158 - - [28/Feb/2008:12:02:22 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET / HTTP/1.0" 200 4447 +91.121.19.229 - - [28/Feb/2008:12:05:42 -0600] "GET /index.html HTTP/1.0" 200 4447 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /about.html HTTP/1.0" 200 7890 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /writing.html HTTP/1.0" 200 2871 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /software.html HTTP/1.0" 200 3163 +91.121.19.229 - - [28/Feb/2008:12:05:43 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /python.html HTTP/1.0" 200 18870 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +91.121.19.229 - - [28/Feb/2008:12:05:44 -0600] "GET /training.html HTTP/1.0" 200 6154 +91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /publications.html HTTP/1.0" 200 7758 +91.121.19.229 - - [28/Feb/2008:12:05:45 -0600] "GET /cv.html HTTP/1.0" 200 31798 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +91.121.19.229 - - [28/Feb/2008:12:05:46 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +91.121.19.229 - - [28/Feb/2008:12:05:47 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /papers/Py97/beazley.html HTTP/1.0" 200 31315 +91.121.19.229 - - [28/Feb/2008:12:05:48 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +192.54.144.229 - - [28/Feb/2008:12:06:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +192.54.144.229 - - [28/Feb/2008:12:07:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.54.144.229 - - [28/Feb/2008:12:07:09 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +192.54.144.229 - - [28/Feb/2008:12:07:12 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.11.245 - - [28/Feb/2008:12:09:58 -0600] "GET /dynamic/ HTTP/1.0" 200 5313 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /python.html HTTP/1.0" 200 18870 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +192.33.115.13 - - [28/Feb/2008:12:15:01 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +64.234.244.162 - - [28/Feb/2008:12:16:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.234.244.162 - - [28/Feb/2008:12:16:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.232.14 - - [28/Feb/2008:12:18:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.234.244.162 - - [28/Feb/2008:12:22:02 -0600] "GET /ply/README HTTP/1.1" 200 8605 +76.222.192.62 - - [28/Feb/2008:12:26:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +76.222.192.62 - - [28/Feb/2008:12:26:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.56.210.71 - - [28/Feb/2008:12:26:43 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.56.210.71 - - [28/Feb/2008:12:26:45 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +74.56.210.71 - - [28/Feb/2008:12:26:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [28/Feb/2008:12:28:18 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +24.15.187.198 - - [28/Feb/2008:12:28:25 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 200 1981 +76.222.192.62 - - [28/Feb/2008:12:28:41 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.56.210.71 - - [28/Feb/2008:12:28:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.118 - - [28/Feb/2008:12:29:27 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE048.HTM HTTP/1.1" 304 - +132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +132.206.52.79 - - [28/Feb/2008:12:31:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:31:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:33:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +76.28.208.159 - - [28/Feb/2008:12:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +132.206.52.79 - - [28/Feb/2008:12:35:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +132.206.52.79 - - [28/Feb/2008:12:36:02 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +132.206.52.79 - - [28/Feb/2008:12:36:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +199.88.143.1 - - [28/Feb/2008:12:36:51 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 200 288790 +74.56.210.71 - - [28/Feb/2008:12:37:01 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.118.156.49 - - [28/Feb/2008:12:39:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.118.156.49 - - [28/Feb/2008:12:39:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.118.156.49 - - [28/Feb/2008:12:39:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.118.156.49 - - [28/Feb/2008:12:39:25 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +71.57.91.136 - - [28/Feb/2008:12:39:33 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 200 248228 +71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:12:39:34 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET / HTTP/1.1" 200 4447 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.24.9 - - [28/Feb/2008:12:41:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [28/Feb/2008:12:41:19 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5313 +128.135.24.9 - - [28/Feb/2008:12:41:23 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /python.html HTTP/1.1" 200 18870 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +208.97.218.10 - - [28/Feb/2008:12:50:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /python.html HTTP/1.1" 304 - +208.97.218.10 - - [28/Feb/2008:12:50:42 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - +84.110.206.115 - - [28/Feb/2008:12:50:59 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +208.97.218.10 - - [28/Feb/2008:12:51:01 -0600] "GET /software.html HTTP/1.1" 200 3163 +84.110.206.115 - - [28/Feb/2008:12:51:04 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1247 +76.28.208.159 - - [28/Feb/2008:12:51:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:12:52:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +170.252.54.132 - - [28/Feb/2008:12:54:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +84.110.189.94 - - [28/Feb/2008:12:56:01 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.189.94 - - [28/Feb/2008:12:56:03 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1283 +151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +151.200.90.2 - - [28/Feb/2008:13:03:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:03:46 -0600] "GET /ply/ HTTP/1.1" 200 8018 +76.28.208.159 - - [28/Feb/2008:13:03:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +151.200.90.2 - - [28/Feb/2008:13:04:08 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +151.200.90.2 - - [28/Feb/2008:13:04:13 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +76.28.208.159 - - [28/Feb/2008:13:04:17 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +76.28.208.159 - - [28/Feb/2008:13:04:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5313 +128.135.11.245 - - [28/Feb/2008:13:04:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:04:46 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 11008 +67.186.98.20 - - [28/Feb/2008:13:04:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:04:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:05:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.157.119.197 - - [28/Feb/2008:13:05:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.200.90.2 - - [28/Feb/2008:13:06:45 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 127268 +71.57.91.136 - - [28/Feb/2008:13:07:17 -0600] "GET /dynamic/06FilesAndText.pdf HTTP/1.1" 206 181019 +74.56.210.71 - - [28/Feb/2008:13:07:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:12:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:15:32 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:13:17:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:13:18:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:01 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:03 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +210.212.8.60 - - [28/Feb/2008:13:20:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqLinuxSharedLibraries HTTP/1.0" 200 1624 +68.21.20.162 - - [28/Feb/2008:13:21:40 -0600] "GET /dynamic/soln12_1.html HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:13:21:46 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +68.21.20.162 - - [28/Feb/2008:13:21:53 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +76.28.208.159 - - [28/Feb/2008:13:25:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:25:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /cv.html HTTP/1.1" 200 31798 +122.167.72.204 - - [28/Feb/2008:13:27:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:27:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.234.244.162 - - [28/Feb/2008:13:27:21 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 +122.167.72.204 - - [28/Feb/2008:13:29:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:29:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:29:22 -0600] "GET / HTTP/1.1" 200 4447 +122.167.72.204 - - [28/Feb/2008:13:29:25 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +122.167.72.204 - - [28/Feb/2008:13:30:28 -0600] "GET /software.html HTTP/1.1" 200 3163 +122.167.72.204 - - [28/Feb/2008:13:30:35 -0600] "GET /consulting.html HTTP/1.1" 200 8728 +122.167.72.204 - - [28/Feb/2008:13:30:42 -0600] "GET /writing.html HTTP/1.1" 200 2871 +122.167.72.204 - - [28/Feb/2008:13:30:46 -0600] "GET /images/writingheader.gif HTTP/1.1" 200 68033 +122.167.72.204 - - [28/Feb/2008:13:30:48 -0600] "GET /about.html HTTP/1.1" 200 7890 +76.28.208.159 - - [28/Feb/2008:13:30:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:30:56 -0600] "GET /images/superboard.jpg HTTP/1.1" 200 71119 +122.167.72.204 - - [28/Feb/2008:13:30:59 -0600] "GET /images/cm5.jpg HTTP/1.1" 200 36699 +122.167.72.204 - - [28/Feb/2008:13:31:00 -0600] "GET /images/aboutheader.jpg HTTP/1.1" 200 159831 +64.234.244.162 - - [28/Feb/2008:13:31:01 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +122.167.72.204 - - [28/Feb/2008:13:31:04 -0600] "GET /images/NoDoubt1_small.jpg HTTP/1.1" 200 110525 +122.167.72.204 - - [28/Feb/2008:13:32:11 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +67.186.98.20 - - [28/Feb/2008:13:32:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.167.72.204 - - [28/Feb/2008:13:32:30 -0600] "GET /images/davechina.jpg HTTP/1.1" 200 445667 +80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.121.95.184 - - [28/Feb/2008:13:33:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.95.184 - - [28/Feb/2008:13:33:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.121.95.184 - - [28/Feb/2008:13:34:08 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +220.225.53.35 - - [28/Feb/2008:13:34:09 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:34:10 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:35:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:35:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:36:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +220.225.53.35 - - [28/Feb/2008:13:36:32 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [28/Feb/2008:13:41:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:13:46:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +122.167.72.204 - - [28/Feb/2008:13:51:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +76.28.208.159 - - [28/Feb/2008:13:54:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +200.171.224.53 - - [28/Feb/2008:13:54:41 -0600] "GET /ply/ HTTP/1.1" 200 8018 +200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +200.171.224.53 - - [28/Feb/2008:13:54:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.43.246.12 - - [28/Feb/2008:13:55:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +122.167.72.204 - - [28/Feb/2008:13:55:20 -0600] "GET /index.html HTTP/1.1" 200 4447 +122.167.72.204 - - [28/Feb/2008:13:55:27 -0600] "GET /training.html HTTP/1.1" 200 6154 +67.186.98.20 - - [28/Feb/2008:13:57:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.64.212.223 - - [28/Feb/2008:13:59:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +81.64.212.223 - - [28/Feb/2008:13:59:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +81.64.212.223 - - [28/Feb/2008:13:59:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +198.175.55.5 - - [28/Feb/2008:13:59:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +90.53.107.51 - - [28/Feb/2008:14:01:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:14:06:00 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.2.96.93 - - [28/Feb/2008:14:12:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +83.2.96.93 - - [28/Feb/2008:14:12:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.166.139.21 - - [28/Feb/2008:14:12:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.166.139.21 - - [28/Feb/2008:14:12:36 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +65.166.139.21 - - [28/Feb/2008:14:12:46 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.1" 200 3162 +65.166.139.21 - - [28/Feb/2008:14:13:40 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +61.247.217.38 - - [28/Feb/2008:14:17:31 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [28/Feb/2008:14:17:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +67.186.98.20 - - [28/Feb/2008:14:17:56 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.186.98.20 - - [28/Feb/2008:14:19:13 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.49.96.242 - - [28/Feb/2008:14:21:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.49.96.242 - - [28/Feb/2008:14:21:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.49.96.242 - - [28/Feb/2008:14:21:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Java HTTP/1.1" 200 5667 +91.49.96.242 - - [28/Feb/2008:14:22:41 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/UsageProblems HTTP/1.1" 200 2302 +64.233.178.136 - - [28/Feb/2008:14:23:56 -0600] "GET /ply/ HTTP/1.0" 200 8018 +190.18.132.71 - - [28/Feb/2008:14:24:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.105.175.11 - - [28/Feb/2008:14:25:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +128.105.175.11 - - [28/Feb/2008:14:25:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:14:26:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:14:26:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:14:27:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:14:27:48 -0600] "GET /dynamic/assign3.html HTTP/1.1" 200 6798 +84.110.127.32 - - [28/Feb/2008:14:29:41 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.127.32 - - [28/Feb/2008:14:29:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 996 +74.6.31.151 - - [28/Feb/2008:14:31:17 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +209.17.146.129 - - [28/Feb/2008:14:34:43 -0600] "GET /ply/ HTTP/1.1" 304 - +200.55.140.181 - - [28/Feb/2008:14:37:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +200.55.140.181 - - [28/Feb/2008:14:37:44 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:38:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.194.8.73 - - [28/Feb/2008:14:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +129.194.8.73 - - [28/Feb/2008:14:46:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:49:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:49:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:51:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.37.149.27 - - [28/Feb/2008:14:52:42 -0600] "GET /ply/ HTTP/1.1" 304 - +128.135.11.245 - - [28/Feb/2008:14:55:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.11.245 - - [28/Feb/2008:14:56:34 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:14:56:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:14:56:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.99.44.101 - - [28/Feb/2008:14:57:33 -0600] "GET /robots.txt HTTP/1.0" 200 71 +79.180.31.79 - - [28/Feb/2008:14:58:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +79.180.31.79 - - [28/Feb/2008:14:58:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +79.180.31.79 - - [28/Feb/2008:14:58:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.232 - - [28/Feb/2008:15:00:11 -0600] "GET /papers/SIAM97/ HTTP/1.0" 403 212 +128.135.11.245 - - [28/Feb/2008:15:04:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +205.238.131.78 - - [28/Feb/2008:15:04:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:04:46 -0600] "GET /cgi-bin/wiki.pl?WishList HTTP/1.1" 200 7981 +128.135.11.245 - - [28/Feb/2008:15:04:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +205.238.131.78 - - [28/Feb/2008:15:05:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +205.238.131.78 - - [28/Feb/2008:15:05:10 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Ocaml HTTP/1.1" 200 1697 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +205.238.131.78 - - [28/Feb/2008:15:05:15 -0600] "GET /cgi-bin/wiki.pl?OcamlArrayHandling HTTP/1.1" 200 3852 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [28/Feb/2008:15:05:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:15:05:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.125.239 - - [28/Feb/2008:15:05:23 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.125.239 - - [28/Feb/2008:15:05:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +205.238.131.78 - - [28/Feb/2008:15:05:50 -0600] "GET /cgi-bin/wiki.pl?OcamlValueExtraction HTTP/1.1" 200 1965 +74.6.20.207 - - [28/Feb/2008:15:06:26 -0600] "GET /writing.html HTTP/1.0" 304 - +205.238.131.78 - - [28/Feb/2008:15:08:34 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1876 +205.238.131.78 - - [28/Feb/2008:15:08:37 -0600] "GET /cgi-bin/wiki.pl?action=rc&days=90 HTTP/1.1" 200 4762 +67.186.98.20 - - [28/Feb/2008:15:17:40 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.24.9 - - [28/Feb/2008:15:18:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.24.9 - - [28/Feb/2008:15:18:06 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.24.9 - - [28/Feb/2008:15:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.24.9 - - [28/Feb/2008:15:19:37 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.125.239 - - [28/Feb/2008:15:26:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.125.239 - - [28/Feb/2008:15:26:51 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +128.135.11.245 - - [28/Feb/2008:15:29:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.54 - - [28/Feb/2008:15:30:02 -0600] "GET /index.html HTTP/1.1" 200 4447 +62.59.179.107 - - [28/Feb/2008:15:33:39 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12562 +62.59.179.107 - - [28/Feb/2008:15:33:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.59.179.107 - - [28/Feb/2008:15:33:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +62.59.179.107 - - [28/Feb/2008:15:34:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +24.15.187.198 - - [28/Feb/2008:15:35:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +68.61.68.187 - - [28/Feb/2008:15:35:53 -0600] "GET /ply/ HTTP/1.1" 200 8018 +98.193.69.179 - - [28/Feb/2008:15:39:21 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:15:39:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +98.193.69.179 - - [28/Feb/2008:15:39:32 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +24.10.16.193 - - [28/Feb/2008:15:39:51 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:15:39:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:15:39:56 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +192.88.162.35 - - [28/Feb/2008:15:43:00 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Perl HTTP/1.1" 200 8748 +134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +134.163.255.20 - - [28/Feb/2008:15:44:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.174 - - [28/Feb/2008:15:45:54 -0600] "GET /ply/ HTTP/1.0" 304 - +192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET / HTTP/1.1" 200 4447 +192.94.94.106 - - [28/Feb/2008:15:51:40 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +24.10.16.193 - - [28/Feb/2008:16:03:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +24.10.16.193 - - [28/Feb/2008:16:03:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:16:03:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:16:07:22 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [28/Feb/2008:16:07:23 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +65.55.208.118 - - [28/Feb/2008:16:15:33 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE035.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:15:50 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE058.HTM HTTP/1.1" 304 - +128.135.125.239 - - [28/Feb/2008:16:16:49 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.224.122.212 - - [28/Feb/2008:16:17:18 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.224.122.212 - - [28/Feb/2008:16:17:21 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +82.224.122.212 - - [28/Feb/2008:16:17:24 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +139.82.24.80 - - [28/Feb/2008:16:18:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +139.82.24.80 - - [28/Feb/2008:16:18:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.212.77 - - [28/Feb/2008:16:20:36 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +74.6.19.115 - - [28/Feb/2008:16:21:48 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.170 - - [28/Feb/2008:16:21:48 -0600] "GET /ply/ HTTP/1.0" 200 8018 +128.135.125.239 - - [28/Feb/2008:16:23:01 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:16:27:27 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.162 - - [28/Feb/2008:16:27:31 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +66.232.113.62 - - [28/Feb/2008:16:28:34 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +85.185.11.131 - - [28/Feb/2008:16:28:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.135.164.162 - - [28/Feb/2008:16:30:06 -0600] "HEAD /dynamic/07Functional.pdf HTTP/1.1" 200 0 +128.135.164.162 - - [28/Feb/2008:16:30:38 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE025.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:49 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE072.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE120.HTM HTTP/1.1" 304 - +65.55.208.118 - - [28/Feb/2008:16:30:51 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE042.HTM HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:16:31:00 -0600] "HEAD /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 0 +128.135.164.162 - - [28/Feb/2008:16:31:09 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +66.146.214.212 - - [28/Feb/2008:16:36:42 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +66.146.214.212 - - [28/Feb/2008:16:36:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:37:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:37:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.201.197.70 - - [28/Feb/2008:16:38:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.57.91.136 - - [28/Feb/2008:16:40:33 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +71.57.91.136 - - [28/Feb/2008:16:40:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +71.57.91.136 - - [28/Feb/2008:16:40:48 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +151.112.127.22 - - [28/Feb/2008:16:42:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +151.112.127.22 - - [28/Feb/2008:16:42:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +66.146.214.212 - - [28/Feb/2008:16:43:10 -0600] "GET /dynamic/ HTTP/1.1" 304 - +80.229.34.140 - - [28/Feb/2008:16:43:35 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.10.16.193 - - [28/Feb/2008:16:44:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +38.99.13.122 - - [28/Feb/2008:16:45:44 -0600] "GET /robots.txt HTTP/1.0" 200 71 +134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.173.200.39 - - [28/Feb/2008:16:52:56 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.173.200.39 - - [28/Feb/2008:16:52:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.173.200.39 - - [28/Feb/2008:16:53:05 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +128.135.11.245 - - [28/Feb/2008:16:55:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:16:55:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.11.245 - - [28/Feb/2008:16:55:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.25.20 - - [28/Feb/2008:16:55:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.22.26 - - [28/Feb/2008:16:55:54 -0600] "GET /dynamic/syllabus.html HTTP/1.0" 200 4589 +86.101.114.10 - - [28/Feb/2008:16:57:39 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.101.114.10 - - [28/Feb/2008:16:57:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.143.35.16 - - [28/Feb/2008:16:58:07 -0600] "GET /ply/ HTTP/1.1" 304 - +210.143.35.13 - - [28/Feb/2008:16:58:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.11.245 - - [28/Feb/2008:16:58:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +216.145.54.15 - - [28/Feb/2008:17:02:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:22 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +216.145.54.15 - - [28/Feb/2008:17:02:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +216.145.54.15 - - [28/Feb/2008:17:02:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1461 +216.145.54.15 - - [28/Feb/2008:17:02:35 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply HTTP/1.0" 301 230 +74.6.31.165 - - [28/Feb/2008:17:02:36 -0600] "GET /ply/ HTTP/1.0" 200 8018 +209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +209.203.68.2 - - [28/Feb/2008:17:02:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.145.54.15 - - [28/Feb/2008:17:02:51 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +209.203.68.2 - - [28/Feb/2008:17:02:56 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +209.203.68.2 - - [28/Feb/2008:17:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +216.145.54.15 - - [28/Feb/2008:17:03:15 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GettingStarted HTTP/1.1" 200 5892 +216.145.54.15 - - [28/Feb/2008:17:03:23 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +128.135.164.162 - - [28/Feb/2008:17:08:15 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:17:08:47 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +68.37.149.27 - - [28/Feb/2008:17:10:53 -0600] "GET /ply/ply.html HTTP/1.1" 304 - +128.135.194.70 - - [28/Feb/2008:17:18:39 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:17:18:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:17:18:46 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +82.254.123.7 - - [28/Feb/2008:17:19:22 -0600] "GET /ply/ HTTP/1.1" 304 - +128.135.239.73 - - [28/Feb/2008:17:20:36 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +128.135.239.73 - - [28/Feb/2008:17:20:46 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.239.73 - - [28/Feb/2008:17:21:01 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.173 - - [28/Feb/2008:17:22:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +163.181.251.10 - - [28/Feb/2008:17:22:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.207.228.84 - - [28/Feb/2008:17:22:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET / HTTP/1.1" 200 4447 +68.37.149.27 - - [28/Feb/2008:17:23:57 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +68.37.149.27 - - [28/Feb/2008:17:24:00 -0600] "GET /software.html HTTP/1.1" 200 3163 +68.37.149.27 - - [28/Feb/2008:17:24:27 -0600] "GET /swill/index.html HTTP/1.1" 200 3786 +128.135.230.179 - - [28/Feb/2008:17:26:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.179 - - [28/Feb/2008:17:26:35 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /robots.txt HTTP/1.0" 200 71 +207.176.224.244 - - [28/Feb/2008:17:26:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +68.37.149.27 - - [28/Feb/2008:17:26:44 -0600] "GET /swill/Doc/index.html HTTP/1.1" 200 39052 +68.37.149.27 - - [28/Feb/2008:17:26:49 -0600] "GET /swill/exec.html HTTP/1.1" 200 12540 +88.191.19.81 - - [28/Feb/2008:17:27:10 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET / HTTP/1.1" 200 4447 +128.135.164.178 - - [28/Feb/2008:17:28:38 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.164.178 - - [28/Feb/2008:17:28:43 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.164.178 - - [28/Feb/2008:17:28:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +202.213.221.97 - - [28/Feb/2008:17:28:55 -0600] "GET /robots.txt? HTTP/1.0" 200 71 +202.213.221.97 - - [28/Feb/2008:17:30:29 -0600] "GET / HTTP/1.0" 200 4447 +75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +75.42.252.241 - - [28/Feb/2008:17:31:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +75.42.252.241 - - [28/Feb/2008:17:31:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +75.42.252.241 - - [28/Feb/2008:17:31:09 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.20.135 - - [28/Feb/2008:17:31:29 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE095.HTM HTTP/1.0" 200 1654 +71.201.41.248 - - [28/Feb/2008:17:33:24 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +74.6.20.205 - - [28/Feb/2008:17:35:59 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:17:36:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:27 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:37:41 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.207.228.84 - - [28/Feb/2008:17:38:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.207.228.84 - - [28/Feb/2008:17:38:17 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.197.57 - - [28/Feb/2008:17:39:41 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +210.9.32.205 - - [28/Feb/2008:17:39:59 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +210.9.32.205 - - [28/Feb/2008:17:40:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [28/Feb/2008:17:40:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.193 - - [28/Feb/2008:17:40:21 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.193 - - [28/Feb/2008:17:40:25 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +67.195.58.168 - - [28/Feb/2008:17:42:57 -0600] "GET /consulting.html HTTP/1.0" 304 - +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET / HTTP/1.1" 200 4447 +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +128.135.194.63 - - [28/Feb/2008:17:43:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /python.html HTTP/1.1" 200 18870 +128.135.194.63 - - [28/Feb/2008:17:43:18 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /dynamic/assign2.html HTTP/1.1" 200 4907 +70.128.13.131 - - [28/Feb/2008:17:43:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.63 - - [28/Feb/2008:17:43:44 -0600] "GET /training.html HTTP/1.1" 200 6154 +128.135.194.63 - - [28/Feb/2008:17:43:47 -0600] "GET /index.html HTTP/1.1" 200 4447 +128.135.194.63 - - [28/Feb/2008:17:43:57 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.194.63 - - [28/Feb/2008:17:44:03 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +193.47.80.43 - - [28/Feb/2008:17:44:19 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.47.80.43 - - [28/Feb/2008:17:44:20 -0600] "GET / HTTP/1.1" 200 4447 +67.195.58.188 - - [28/Feb/2008:17:44:27 -0600] "GET /training.html HTTP/1.0" 200 6154 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.93.186.133 - - [28/Feb/2008:17:45:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.178 - - [28/Feb/2008:17:45:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 145188 +128.135.194.178 - - [28/Feb/2008:17:45:53 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 327437 +67.195.44.110 - - [28/Feb/2008:17:50:54 -0600] "GET /robots.txt HTTP/1.0" 200 71 +128.135.230.204 - - [28/Feb/2008:17:51:27 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.197.195 - - [28/Feb/2008:17:53:36 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.197.195 - - [28/Feb/2008:17:53:44 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.163.43.58 - - [28/Feb/2008:17:57:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:06 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.143.136.157 - - [28/Feb/2008:18:11:21 -0600] "GET /ply/README HTTP/1.1" 200 8605 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.143.136.157 - - [28/Feb/2008:18:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:45 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.143.136.157 - - [28/Feb/2008:18:11:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [28/Feb/2008:18:11:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +201.236.226.90 - - [28/Feb/2008:18:14:23 -0600] "GET /dynamic/syllabus.html HTTP/1.1" 200 4589 +202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.53 - - [28/Feb/2008:18:16:15 -0600] "GET /ply/index.html HTTP/1.1" 200 8018 +80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.58.205.45 - - [28/Feb/2008:18:17:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [28/Feb/2008:18:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [28/Feb/2008:18:25:26 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.197.195 - - [28/Feb/2008:18:29:16 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.197.195 - - [28/Feb/2008:18:29:24 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic HTTP/1.1" 301 246 +128.135.197.57 - - [28/Feb/2008:18:29:43 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.197.57 - - [28/Feb/2008:18:29:49 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +189.13.67.152 - - [28/Feb/2008:18:30:08 -0600] "GET /ply/ HTTP/1.1" 200 8018 +189.13.67.152 - - [28/Feb/2008:18:30:10 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +218.111.4.247 - - [28/Feb/2008:18:30:29 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +211.127.232.14 - - [28/Feb/2008:18:31:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.194.70 - - [28/Feb/2008:18:31:24 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +211.127.232.14 - - [28/Feb/2008:18:31:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +128.135.194.70 - - [28/Feb/2008:18:31:29 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +211.127.232.14 - - [28/Feb/2008:18:31:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CplusPlus HTTP/1.1" 200 4432 +211.127.232.14 - - [28/Feb/2008:18:31:37 -0600] "GET /cgi-bin/wiki.pl?SwigFaqCPlusPlusUnresolvedSymbols HTTP/1.1" 200 2847 +211.127.232.14 - - [28/Feb/2008:18:32:50 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1161 +221.189.180.200 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +211.127.232.14 - - [28/Feb/2008:18:32:54 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo/GoogleSummerOfCode HTTP/1.1" 200 6020 +65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /robots.txt HTTP/1.0" 200 71 +65.55.212.77 - - [28/Feb/2008:18:33:57 -0600] "GET /ply/README HTTP/1.0" 200 8605 +161.45.160.30 - - [28/Feb/2008:18:33:58 -0600] "GET /ply/ HTTP/1.1" 200 8018 +161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +161.45.160.30 - - [28/Feb/2008:18:34:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +161.45.160.30 - - [28/Feb/2008:18:35:26 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +161.45.160.30 - - [28/Feb/2008:18:35:28 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +161.45.160.30 - - [28/Feb/2008:18:35:30 -0600] "GET /ply/README HTTP/1.1" 200 8605 +189.13.67.152 - - [28/Feb/2008:18:36:18 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +189.13.67.152 - - [28/Feb/2008:18:36:20 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.195.58.172 - - [28/Feb/2008:18:42:17 -0600] "GET /index.html HTTP/1.0" 200 4447 +128.135.230.204 - - [28/Feb/2008:18:53:45 -0600] "GET /old/index.html HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:49 -0600] "GET /old/ HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:56 -0600] "GET /old/cource18.html HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:18:53:59 -0600] "GET / HTTP/1.1" 200 4447 +128.135.230.204 - - [28/Feb/2008:18:54:00 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.195.58.177 - - [28/Feb/2008:18:55:27 -0600] "GET /python.html HTTP/1.0" 304 - +61.135.190.17 - - [28/Feb/2008:18:56:59 -0600] "GET /ply/ HTTP/1.1" 304 - +64.124.85.74 - - [28/Feb/2008:18:59:55 -0600] "GET /robots.txt HTTP/1.1" 200 71 +64.124.85.74 - - [28/Feb/2008:19:02:09 -0600] "GET /ply/ HTTP/1.1" 200 8018 +128.135.197.195 - - [28/Feb/2008:19:02:31 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +128.135.197.195 - - [28/Feb/2008:19:02:38 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +128.135.164.162 - - [28/Feb/2008:19:08:39 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +68.81.244.149 - - [28/Feb/2008:19:12:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +128.135.230.3 - - [28/Feb/2008:19:15:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.230.3 - - [28/Feb/2008:19:15:40 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.3 - - [28/Feb/2008:19:15:47 -0600] "GET /dynamic/ HTTP/1.1" 304 - +128.135.230.3 - - [28/Feb/2008:19:15:49 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +128.135.230.161 - - [28/Feb/2008:19:17:07 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +128.135.230.161 - - [28/Feb/2008:19:17:37 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +98.215.100.68 - - [28/Feb/2008:19:19:35 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +98.215.100.68 - - [28/Feb/2008:19:20:12 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +212.56.88.112 - - [28/Feb/2008:19:20:53 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.201.176.194 - - [28/Feb/2008:19:21:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.108 - - [28/Feb/2008:19:22:54 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +74.6.19.115 - - [28/Feb/2008:19:24:20 -0600] "GET /robots.txt HTTP/1.0" 200 71 +74.6.31.145 - - [28/Feb/2008:19:24:20 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +213.224.182.16 - - [28/Feb/2008:19:24:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +213.224.182.16 - - [28/Feb/2008:19:24:38 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +212.56.88.112 - - [28/Feb/2008:19:26:15 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +161.45.160.30 - - [28/Feb/2008:19:26:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.114 - - [28/Feb/2008:19:26:57 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +161.45.162.52 - - [28/Feb/2008:19:26:58 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +213.224.182.16 - - [28/Feb/2008:19:29:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.9.32.205 - - [28/Feb/2008:19:32:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.165 - - [28/Feb/2008:19:32:19 -0600] "GET /writing.html HTTP/1.0" 200 2871 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.83.111.28 - - [28/Feb/2008:19:33:29 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +67.83.111.28 - - [28/Feb/2008:19:33:34 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +128.135.194.70 - - [28/Feb/2008:19:34:23 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +161.45.162.52 - - [28/Feb/2008:19:38:50 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +202.213.221.97 - - [28/Feb/2008:19:39:18 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +210.9.32.205 - - [28/Feb/2008:19:39:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.56.88.112 - - [28/Feb/2008:19:40:06 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +67.195.34.111 - - [28/Feb/2008:19:44:14 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.111 - - [28/Feb/2008:19:44:17 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.195.58.158 - - [28/Feb/2008:19:45:40 -0600] "GET /ply/ply.html HTTP/1.0" 304 - +134.159.131.34 - - [28/Feb/2008:19:45:49 -0600] "GET /ply/ HTTP/1.1" 200 8018 +134.159.131.34 - - [28/Feb/2008:19:45:50 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.159.131.34 - - [28/Feb/2008:19:45:51 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +134.159.131.34 - - [28/Feb/2008:19:45:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply HTTP/1.0" 301 230 +67.195.34.97 - - [28/Feb/2008:19:46:39 -0600] "GET /ply/ HTTP/1.0" 200 8018 +67.195.58.181 - - [28/Feb/2008:19:50:24 -0600] "GET /ply/support.html HTTP/1.0" 200 739 +67.195.58.151 - - [28/Feb/2008:19:50:37 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +67.195.58.170 - - [28/Feb/2008:19:50:56 -0600] "GET /ply/ply-1.4.tar.gz HTTP/1.0" 200 66002 +122.152.129.53 - - [28/Feb/2008:19:51:51 -0600] "GET /robots.txt HTTP/1.1" 200 71 +212.56.88.112 - - [28/Feb/2008:19:52:55 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:16 -0600] "GET /dynamic/lecture8 HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:21 -0600] "GET /dynamic/lecture8/ HTTP/1.1" 404 133 +128.135.230.204 - - [28/Feb/2008:19:57:30 -0600] "GET /dynamic/Lecture8/ HTTP/1.1" 404 133 +128.135.230.179 - - [28/Feb/2008:20:03:24 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.230.179 - - [28/Feb/2008:20:03:29 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +212.56.88.112 - - [28/Feb/2008:20:15:42 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +66.232.113.194 - - [28/Feb/2008:20:22:08 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +213.227.137.187 - - [28/Feb/2008:20:22:09 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [28/Feb/2008:20:22:14 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +195.50.147.66 - - [28/Feb/2008:20:25:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +195.50.147.66 - - [28/Feb/2008:20:25:31 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.116.72.114 - - [28/Feb/2008:20:27:57 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +66.116.72.114 - - [28/Feb/2008:20:28:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +122.152.140.206 - - [28/Feb/2008:20:32:56 -0600] "GET / HTTP/1.1" 200 4447 +201.16.201.21 - - [28/Feb/2008:20:38:55 -0600] "GET /ply/ HTTP/1.1" 200 8018 +201.16.201.21 - - [28/Feb/2008:20:38:57 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.16.201.21 - - [28/Feb/2008:20:38:58 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.154 - - [28/Feb/2008:20:40:17 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +128.135.164.154 - - [28/Feb/2008:20:40:33 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +128.135.164.154 - - [28/Feb/2008:20:42:02 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +204.246.129.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +65.46.48.194 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +202.44.78.196 - - [28/Feb/2008:20:42:37 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +202.44.78.196 - - [28/Feb/2008:20:43:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +202.44.78.196 - - [28/Feb/2008:20:43:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Python HTTP/1.1" 200 11575 +206.51.237.114 - - [28/Feb/2008:20:44:26 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +80.191.131.2 - - [28/Feb/2008:20:44:29 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.185.116.11 - - [28/Feb/2008:20:44:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +128.135.164.154 - - [28/Feb/2008:20:46:24 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 304 - +82.189.46.148 - - [28/Feb/2008:20:46:59 -0600] "GET / HTTP/1.0" 200 4447 +82.189.46.148 - - [28/Feb/2008:20:47:00 -0600] "GET /about.html HTTP/1.0" 200 7890 +82.189.46.148 - - [28/Feb/2008:20:47:10 -0600] "GET /training.html HTTP/1.0" 200 6154 +82.189.46.148 - - [28/Feb/2008:20:47:19 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +82.189.46.148 - - [28/Feb/2008:20:47:21 -0600] "GET /python.html HTTP/1.0" 200 18870 +82.189.46.148 - - [28/Feb/2008:20:47:23 -0600] "GET /consulting.html HTTP/1.0" 200 8728 +82.189.46.148 - - [28/Feb/2008:20:47:28 -0600] "GET /writing.html HTTP/1.0" 200 2871 +82.189.46.148 - - [28/Feb/2008:20:47:29 -0600] "GET /software.html HTTP/1.0" 200 3163 +82.189.46.148 - - [28/Feb/2008:20:47:30 -0600] "GET /index.html HTTP/1.0" 200 4447 +82.189.46.148 - - [28/Feb/2008:20:47:32 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +82.189.46.148 - - [28/Feb/2008:20:47:35 -0600] "GET /cv.html HTTP/1.0" 200 31798 +82.189.46.148 - - [28/Feb/2008:20:47:38 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +82.189.46.148 - - [28/Feb/2008:20:47:40 -0600] "GET /ply/README HTTP/1.0" 200 8605 +82.189.46.148 - - [28/Feb/2008:20:47:42 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +82.189.46.148 - - [28/Feb/2008:20:47:43 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +82.189.46.148 - - [28/Feb/2008:20:47:45 -0600] "GET /swill/exec.html HTTP/1.0" 200 12540 +67.195.44.110 - - [28/Feb/2008:20:49:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.44.109 - - [28/Feb/2008:20:49:43 -0600] "GET /ply/ HTTP/1.0" 200 8018 +74.6.28.237 - - [28/Feb/2008:20:50:01 -0600] "GET /photos/wind/pages/IMG_1309.htm HTTP/1.0" 404 133 +128.135.164.154 - - [28/Feb/2008:20:50:18 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +128.135.164.154 - - [28/Feb/2008:20:50:59 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +74.6.25.125 - - [28/Feb/2008:20:51:20 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE106.HTM HTTP/1.0" 200 1298 +128.135.164.154 - - [28/Feb/2008:20:55:56 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +219.142.125.240 - - [28/Feb/2008:20:55:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +219.142.125.240 - - [28/Feb/2008:20:56:06 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +219.142.125.240 - - [28/Feb/2008:20:56:17 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +201.16.201.21 - - [28/Feb/2008:20:57:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +128.135.164.154 - - [28/Feb/2008:20:59:38 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - +128.135.164.154 - - [28/Feb/2008:20:59:44 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 206 416530 +219.142.125.240 - - [28/Feb/2008:21:01:44 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +65.55.208.117 - - [28/Feb/2008:21:03:24 -0600] "GET /robots.txt HTTP/1.1" 200 71 +217.196.43.134 - - [28/Feb/2008:21:05:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.217.219.85 - - [28/Feb/2008:21:17:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +202.179.180.52 - - [28/Feb/2008:21:18:31 -0600] "GET /robots.txt HTTP/1.1" 200 71 +202.179.180.52 - - [28/Feb/2008:21:18:33 -0600] "GET /papers/Perl98/swigperl.pdf HTTP/1.1" 200 151655 +77.91.224.3 - - [28/Feb/2008:21:20:30 -0600] "GET /robots.txt HTTP/1.1" 200 71 +71.57.91.136 - - [28/Feb/2008:21:20:30 -0600] "GET /dynamic/ HTTP/1.1" 304 - +77.91.224.3 - - [28/Feb/2008:21:20:31 -0600] "GET / HTTP/1.1" 200 4447 +77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /robots.txt HTTP/1.1" 200 71 +77.91.224.13 - - [28/Feb/2008:21:22:54 -0600] "GET /ply/ HTTP/1.1" 200 8018 +211.127.232.14 - - [28/Feb/2008:21:22:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +84.110.148.125 - - [28/Feb/2008:21:23:39 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.187.74 - - [28/Feb/2008:21:23:40 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.148.125 - - [28/Feb/2008:21:23:41 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1029 +84.110.187.74 - - [28/Feb/2008:21:23:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 975 +24.37.1.68 - - [28/Feb/2008:21:23:46 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +24.37.1.68 - - [28/Feb/2008:21:23:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.37.1.68 - - [28/Feb/2008:21:23:51 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +211.127.232.14 - - [28/Feb/2008:21:25:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +67.176.197.254 - - [28/Feb/2008:21:27:07 -0600] "GET / HTTP/1.1" 200 4447 +207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /cgi-bin/wiki.pl?S HTTP/1.1" 200 1163 +207.47.98.129 - - [28/Feb/2008:21:27:11 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:27:12 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +67.176.197.254 - - [28/Feb/2008:21:27:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:27:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +207.47.98.129 - - [28/Feb/2008:21:27:28 -0600] "GET /cgi-bin/wiki.pl? HTTP/1.1" 200 2883 +207.47.98.129 - - [28/Feb/2008:21:27:36 -0600] "GET /cgi-bin/wiki.pl?DeveloperInfo HTTP/1.1" 200 3015 +211.127.232.14 - - [28/Feb/2008:21:27:40 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 +207.47.98.129 - - [28/Feb/2008:21:27:42 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 4177 +211.127.232.14 - - [28/Feb/2008:21:27:48 -0600] "GET /cgi-bin/wiki.pl?action=history&id=SwigFaqDLLUsingMingw HTTP/1.1" 200 2787 +207.47.98.129 - - [28/Feb/2008:21:28:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Module HTTP/1.1" 200 9008 +207.47.98.129 - - [28/Feb/2008:21:28:51 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 2665 +71.57.91.136 - - [28/Feb/2008:21:28:56 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 304 - +207.47.98.129 - - [28/Feb/2008:21:28:57 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +71.57.91.136 - - [28/Feb/2008:21:28:57 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +70.41.192.171 - - [28/Feb/2008:21:34:03 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.41.192.171 - - [28/Feb/2008:21:34:04 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.176.197.254 - - [28/Feb/2008:21:39:46 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +67.195.58.164 - - [28/Feb/2008:21:43:01 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 +193.252.149.16 - - [28/Feb/2008:21:44:01 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [28/Feb/2008:21:44:07 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.1" 200 75085 +84.110.177.245 - - [28/Feb/2008:21:45:48 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.177.245 - - [28/Feb/2008:21:45:49 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 981 +70.41.192.171 - - [28/Feb/2008:21:52:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.232.113.62 - - [28/Feb/2008:22:03:29 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +83.238.23.214 - - [28/Feb/2008:22:03:31 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +84.110.153.190 - - [28/Feb/2008:22:05:21 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.153.190 - - [28/Feb/2008:22:05:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1205 +24.12.12.183 - - [28/Feb/2008:22:08:26 -0600] "GET / HTTP/1.1" 200 4447 +24.12.12.183 - - [28/Feb/2008:22:08:27 -0600] "GET /images/Davetubes.jpg HTTP/1.1" 200 60025 +24.12.12.183 - - [28/Feb/2008:22:08:29 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +24.12.12.183 - - [28/Feb/2008:22:08:33 -0600] "GET /dynamic/project.html HTTP/1.1" 304 - +64.135.175.82 - - [28/Feb/2008:22:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +64.135.175.82 - - [28/Feb/2008:22:09:27 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.135.175.82 - - [28/Feb/2008:22:09:54 -0600] "GET /cgi-bin/wiki.pl?back=/SharedLibraries HTTP/1.1" 200 1111 +210.245.31.3 - - [28/Feb/2008:22:10:05 -0600] "GET /ply/ HTTP/1.1" 200 8018 +210.245.31.3 - - [28/Feb/2008:22:10:07 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +210.245.31.3 - - [28/Feb/2008:22:10:08 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.135.175.82 - - [28/Feb/2008:22:11:02 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +24.12.12.183 - - [28/Feb/2008:22:11:06 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +70.41.192.171 - - [28/Feb/2008:22:19:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:12 -0600] "GET /ply/ HTTP/1.1" 200 8018 +64.24.133.241 - - [28/Feb/2008:22:20:15 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +64.24.133.241 - - [28/Feb/2008:22:20:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.24.133.241 - - [28/Feb/2008:22:20:47 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +74.6.24.112 - - [28/Feb/2008:22:26:05 -0600] "GET /diversions.html HTTP/1.0" 200 2427 +67.195.58.186 - - [28/Feb/2008:22:32:25 -0600] "GET /ply/ply-1.2.tar.gz HTTP/1.0" 200 64334 +67.176.197.254 - - [28/Feb/2008:22:35:53 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.176.197.254 - - [28/Feb/2008:22:35:54 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +131.215.42.190 - - [28/Feb/2008:22:41:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:22:41:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.110.220.16 - - [28/Feb/2008:22:44:26 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +131.215.42.190 - - [28/Feb/2008:22:46:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:22:46:46 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/ HTTP/1.1" 200 8018 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:48:47 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:49:43 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [28/Feb/2008:22:53:36 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +24.1.247.118 - - [28/Feb/2008:22:53:47 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +216.19.182.199 - - [28/Feb/2008:22:54:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +216.19.182.199 - - [28/Feb/2008:22:57:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:38 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:39 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:02:52 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +67.195.58.164 - - [28/Feb/2008:23:02:55 -0600] "GET /ply/ply-1.7.tar.gz HTTP/1.0" 200 75085 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.247.118 - - [28/Feb/2008:23:04:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.1.159.241 - - [28/Feb/2008:23:08:43 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 200 343614 +68.37.149.27 - - [28/Feb/2008:23:11:27 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 200 133908 +24.1.159.241 - - [28/Feb/2008:23:12:57 -0600] "GET /dynamic/07Functional.pdf HTTP/1.1" 206 96799 +68.37.149.27 - - [28/Feb/2008:23:13:48 -0600] "GET /ply/ HTTP/1.1" 200 8018 +24.10.16.193 - - [28/Feb/2008:23:19:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +24.15.187.198 - - [28/Feb/2008:23:22:45 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +131.215.42.190 - - [28/Feb/2008:23:24:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +131.215.42.190 - - [28/Feb/2008:23:24:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:23:26:49 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +68.37.149.27 - - [28/Feb/2008:23:28:32 -0600] "GET /ply/ HTTP/1.1" 200 8018 +131.215.42.190 - - [28/Feb/2008:23:29:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.122 - - [28/Feb/2008:23:30:41 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE011.HTM HTTP/1.1" 200 1466 +131.215.42.190 - - [28/Feb/2008:23:32:49 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +71.132.65.31 - - [28/Feb/2008:23:34:06 -0600] "GET /ply/ HTTP/1.1" 200 8018 +71.132.65.31 - - [28/Feb/2008:23:34:09 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +71.132.65.31 - - [28/Feb/2008:23:34:10 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +68.37.149.27 - - [28/Feb/2008:23:35:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +131.215.42.190 - - [28/Feb/2008:23:36:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.195.58.182 - - [28/Feb/2008:23:37:18 -0600] "GET /ply/ply-1.1.tar.gz HTTP/1.0" 200 62496 +24.1.159.241 - - [28/Feb/2008:23:42:48 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.1" 206 339721 +131.215.42.190 - - [28/Feb/2008:23:43:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +131.215.42.190 - - [28/Feb/2008:23:43:20 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1145 +131.215.42.190 - - [28/Feb/2008:23:43:22 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +131.215.42.190 - - [28/Feb/2008:23:43:28 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.1" 200 2883 +131.215.42.190 - - [28/Feb/2008:23:43:32 -0600] "GET /cgi-bin/wiki.pl?back=SwigFaqDLLForWindows HTTP/1.1" 200 1152 +131.215.42.190 - - [28/Feb/2008:23:43:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +131.215.42.190 - - [28/Feb/2008:23:43:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +131.215.42.190 - - [28/Feb/2008:23:43:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingMingw HTTP/1.1" 200 2737 +203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 200 15264 +203.20.35.28 - - [28/Feb/2008:23:55:52 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +203.20.35.28 - - [28/Feb/2008:23:55:55 -0600] "GET /python/tutorial/beazley_intro_python/intropy.pdf HTTP/1.1" 206 188184 +61.14.187.142 - - [29/Feb/2008:00:04:15 -0600] "GET / HTTP/1.0" 200 4447 +61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /python.html HTTP/1.0" 200 18870 +61.14.187.142 - - [29/Feb/2008:00:04:16 -0600] "GET /index.html HTTP/1.0" 200 4447 +61.14.187.142 - - [29/Feb/2008:00:04:17 -0600] "GET /dynamic/index.html HTTP/1.0" 200 5653 +61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /ply/index.html HTTP/1.0" 200 8018 +61.14.187.142 - - [29/Feb/2008:00:04:18 -0600] "GET /writing.html HTTP/1.0" 200 2871 +61.14.187.142 - - [29/Feb/2008:00:04:19 -0600] "GET /about.html HTTP/1.0" 200 7890 +61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /software.html HTTP/1.0" 200 3163 +61.14.187.142 - - [29/Feb/2008:00:04:20 -0600] "GET /training.html HTTP/1.0" 200 6154 +61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign1.html HTTP/1.0" 200 3047 +61.14.187.142 - - [29/Feb/2008:00:04:21 -0600] "GET /dynamic/assign3.html HTTP/1.0" 200 6798 +217.172.44.82 - - [29/Feb/2008:00:04:21 -0600] "GET /ply/ HTTP/1.1" 200 8018 +61.14.187.142 - - [29/Feb/2008:00:04:23 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /ply/README HTTP/1.0" 200 8605 +61.14.187.142 - - [29/Feb/2008:00:04:24 -0600] "GET /per_secrets.html HTTP/1.0" 200 7958 +61.14.187.142 - - [29/Feb/2008:00:04:25 -0600] "GET /publications.html HTTP/1.0" 200 7758 +61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /sysop.html HTTP/1.0" 200 1760 +61.14.187.142 - - [29/Feb/2008:00:04:26 -0600] "GET /swill/index.html HTTP/1.0" 200 3786 +61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/portfolio.txt HTTP/1.0" 200 100 +61.14.187.142 - - [29/Feb/2008:00:04:27 -0600] "GET /dynamic/sd.html HTTP/1.0" 200 1873 +61.14.187.142 - - [29/Feb/2008:00:04:28 -0600] "GET /papers/Py96/python96.html HTTP/1.0" 200 22442 +61.14.187.142 - - [29/Feb/2008:00:04:29 -0600] "GET /swill/about.html HTTP/1.0" 404 133 +67.97.80.5 - - [29/Feb/2008:00:16:24 -0600] "GET /python.html HTTP/1.0" 200 18870 +67.97.80.5 - - [29/Feb/2008:00:16:25 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 200 99542 +67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /python.html HTTP/1.0" 304 - +67.97.80.5 - - [29/Feb/2008:00:17:23 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.0" 304 - +67.97.80.5 - - [29/Feb/2008:00:17:27 -0600] "GET /training.html HTTP/1.0" 200 6154 +213.145.165.82 - - [29/Feb/2008:00:17:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +84.48.187.205 - - [29/Feb/2008:00:24:52 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +125.19.42.35 - - [29/Feb/2008:00:26:35 -0600] "GET /cv.html HTTP/1.1" 200 31798 +125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.19.42.35 - - [29/Feb/2008:00:26:36 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.19.42.35 - - [29/Feb/2008:00:29:38 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.16.133.35 - - [29/Feb/2008:00:32:07 -0600] "GET /python.html HTTP/1.1" 200 18870 +125.16.133.35 - - [29/Feb/2008:00:32:10 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 200 99542 +125.19.42.35 - - [29/Feb/2008:00:32:42 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.252.149.15 - - [29/Feb/2008:00:33:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.15 - - [29/Feb/2008:00:33:36 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /python.html HTTP/1.1" 304 - +125.16.133.35 - - [29/Feb/2008:00:38:11 -0600] "GET /images/NerdRanchEurope.jpg HTTP/1.1" 304 - +67.186.98.20 - - [29/Feb/2008:00:54:58 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [29/Feb/2008:00:54:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.26.100 - - [29/Feb/2008:00:58:05 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE115.HTM HTTP/1.0" 200 1596 +74.6.29.23 - - [29/Feb/2008:01:15:57 -0600] "GET /dynamic/ HTTP/1.0" 200 5653 +61.57.149.13 - - [29/Feb/2008:01:17:56 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +61.57.149.13 - - [29/Feb/2008:01:17:57 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.149.13 - - [29/Feb/2008:01:17:59 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +61.57.149.13 - - [29/Feb/2008:01:18:00 -0600] "GET /cgi-bin/wiki.pl?RecentChanges HTTP/1.1" 200 1869 +74.6.28.122 - - [29/Feb/2008:01:22:52 -0600] "GET /dynamic/08GeneratorNetwork.pdf HTTP/1.0" 200 343614 +193.252.149.16 - - [29/Feb/2008:01:24:40 -0600] "GET /robots.txt HTTP/1.1" 200 71 +193.252.149.16 - - [29/Feb/2008:01:24:44 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +78.99.69.34 - - [29/Feb/2008:01:29:11 -0600] "GET /ply/ HTTP/1.1" 200 8018 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +78.99.69.34 - - [29/Feb/2008:01:29:12 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +78.99.69.34 - - [29/Feb/2008:01:29:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.116 - - [29/Feb/2008:01:30:04 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE003.HTM HTTP/1.1" 304 - +38.98.120.84 - - [29/Feb/2008:01:49:20 -0600] "GET /robots.txt HTTP/1.1" 200 71 +38.98.120.84 - - [29/Feb/2008:01:49:22 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [29/Feb/2008:01:49:23 -0600] "GET /about.html HTTP/1.1" 200 7890 +38.98.120.84 - - [29/Feb/2008:01:49:24 -0600] "GET / HTTP/1.1" 200 4447 +38.98.120.84 - - [29/Feb/2008:01:49:25 -0600] "GET /sitemap.html HTTP/1.1" 404 133 +38.98.120.84 - - [29/Feb/2008:01:49:27 -0600] "GET / HTTP/1.1" 200 4447 +80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +80.58.205.45 - - [29/Feb/2008:01:53:50 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +221.239.94.225 - - [29/Feb/2008:02:02:52 -0600] "GET /ply/ HTTP/1.1" 200 8018 +221.239.94.225 - - [29/Feb/2008:02:02:54 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.112.65.218 - - [29/Feb/2008:02:03:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.112.65.218 - - [29/Feb/2008:02:03:04 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +91.112.65.218 - - [29/Feb/2008:02:03:07 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +91.112.65.218 - - [29/Feb/2008:02:03:14 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.112.65.218 - - [29/Feb/2008:02:03:19 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +91.112.65.218 - - [29/Feb/2008:02:04:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/CSharp HTTP/1.1" 200 5371 +140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +140.128.18.187 - - [29/Feb/2008:02:09:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.128.18.187 - - [29/Feb/2008:02:09:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +140.128.18.187 - - [29/Feb/2008:02:09:26 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.120 - - [29/Feb/2008:02:09:34 -0600] "GET /photos/wind/pages/IMG_1267.htm HTTP/1.1" 404 133 +91.49.96.242 - - [29/Feb/2008:02:12:12 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +91.49.96.242 - - [29/Feb/2008:02:12:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.75.252.165 - - [29/Feb/2008:02:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +210.75.252.165 - - [29/Feb/2008:02:14:23 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +210.75.252.165 - - [29/Feb/2008:02:14:33 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +64.81.229.55 - - [29/Feb/2008:02:16:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +64.81.229.55 - - [29/Feb/2008:02:18:11 -0600] "GET /dynamic/project.html HTTP/1.1" 200 4209 +210.75.252.165 - - [29/Feb/2008:02:18:18 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +91.112.65.218 - - [29/Feb/2008:02:26:32 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +67.195.58.174 - - [29/Feb/2008:02:28:27 -0600] "GET /ply/ HTTP/1.0" 304 - +91.112.65.218 - - [29/Feb/2008:02:28:52 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/example.html HTTP/1.0" 200 2359 +67.195.58.188 - - [29/Feb/2008:02:28:54 -0600] "GET /ply/ply-2.2.tar.gz HTTP/1.0" 200 142210 +67.195.58.160 - - [29/Feb/2008:02:29:13 -0600] "GET /ply/ply-1.8.tar.gz HTTP/1.0" 200 74610 +67.195.58.178 - - [29/Feb/2008:02:29:25 -0600] "GET /ply/PLYTalk.pdf HTTP/1.0" 200 194510 +85.97.128.179 - - [29/Feb/2008:02:36:27 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +85.97.128.179 - - [29/Feb/2008:02:36:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +85.97.128.179 - - [29/Feb/2008:02:37:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +85.97.128.179 - - [29/Feb/2008:02:37:41 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +65.55.208.118 - - [29/Feb/2008:02:39:03 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.208.118 - - [29/Feb/2008:02:39:06 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE007.HTM HTTP/1.1" 200 1337 +67.195.58.168 - - [29/Feb/2008:02:43:46 -0600] "GET /ply/ply-2.0.tar.gz HTTP/1.0" 200 75765 +67.195.58.175 - - [29/Feb/2008:02:49:18 -0600] "GET /ply/ply-1.5.tar.gz HTTP/1.0" 200 69278 +221.239.94.225 - - [29/Feb/2008:02:53:09 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 9416 +221.239.94.225 - - [29/Feb/2008:02:53:11 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 42184 +221.239.94.225 - - [29/Feb/2008:02:53:12 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 74952 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 29862 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 12582 +221.239.94.225 - - [29/Feb/2008:02:53:13 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 +221.239.94.225 - - [29/Feb/2008:02:53:15 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.1" 206 107720 +121.14.96.153 - - [29/Feb/2008:02:53:18 -0600] "GET /ply/ HTTP/1.1" 200 8018 +58.60.14.236 - - [29/Feb/2008:02:53:19 -0600] "GET / HTTP/1.1" 200 4447 +124.115.1.67 - - [29/Feb/2008:02:53:20 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +121.14.96.152 - - [29/Feb/2008:02:53:22 -0600] "GET /ply/README HTTP/1.1" 200 8605 +58.60.13.231 - - [29/Feb/2008:02:53:32 -0600] "GET /ply/support.html HTTP/1.1" 200 739 +202.58.71.138 - - [29/Feb/2008:02:54:53 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +202.58.71.138 - - [29/Feb/2008:02:54:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +202.58.71.138 - - [29/Feb/2008:02:58:55 -0600] "GET /ply/ HTTP/1.0" 200 8018 +202.58.71.138 - - [29/Feb/2008:02:59:02 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +202.58.71.138 - - [29/Feb/2008:02:59:35 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +210.217.95.253 - - [29/Feb/2008:03:00:31 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +210.217.95.253 - - [29/Feb/2008:03:00:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.1" 200 3601 +82.127.117.160 - - [29/Feb/2008:03:05:32 -0600] "GET /ply/ HTTP/1.0" 200 8018 +82.127.117.160 - - [29/Feb/2008:03:05:33 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +82.127.117.160 - - [29/Feb/2008:03:05:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +84.110.143.135 - - [29/Feb/2008:03:08:58 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.143.135 - - [29/Feb/2008:03:09:08 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 993 +210.51.195.13 - - [29/Feb/2008:03:13:15 -0600] "GET /cgi-bin/wiki.pl HTTP/1.0" 200 2871 +210.51.195.13 - - [29/Feb/2008:03:14:20 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.0" 200 3580 +210.51.195.13 - - [29/Feb/2008:03:14:27 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/Installation HTTP/1.0" 200 3150 +210.51.195.13 - - [29/Feb/2008:03:14:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +60.28.17.44 - - [29/Feb/2008:03:15:03 -0600] "GET / HTTP/1.1" 200 4447 +210.51.195.13 - - [29/Feb/2008:03:19:05 -0600] "GET /cgi-bin/wiki.pl?SwigFaqMakeCheckFails HTTP/1.0" 200 2849 +220.220.204.13 - - [29/Feb/2008:03:21:29 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +220.220.204.13 - - [29/Feb/2008:03:21:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.220.204.13 - - [29/Feb/2008:03:21:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +220.220.204.13 - - [29/Feb/2008:03:21:34 -0600] "GET /cgi-bin/wiki.pl?SwigFaq HTTP/1.1" 200 3592 +221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/ HTTP/1.1" 304 - +221.239.94.225 - - [29/Feb/2008:03:21:38 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 - +220.220.204.13 - - [29/Feb/2008:03:21:46 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 200 1323 +220.220.204.13 - - [29/Feb/2008:03:21:52 -0600] "GET /cgi-bin/wiki.pl?CallbackDirective HTTP/1.1" 200 3269 +220.220.204.13 - - [29/Feb/2008:03:21:57 -0600] "GET /cgi-bin/wiki.pl?AddmethodsDirective HTTP/1.1" 200 1671 +220.220.204.13 - - [29/Feb/2008:03:22:04 -0600] "GET /cgi-bin/wiki.pl?ExtendDirective HTTP/1.1" 200 7231 +67.195.58.178 - - [29/Feb/2008:03:29:56 -0600] "GET /software.html HTTP/1.0" 304 - +67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /dynamic/assign5.html HTTP/1.1" 200 10928 +67.186.98.20 - - [29/Feb/2008:03:31:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +67.186.98.20 - - [29/Feb/2008:03:31:55 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.99.30.64 - - [29/Feb/2008:03:32:41 -0600] "GET /robots.txt HTTP/1.0" 200 71 +91.103.40.50 - - [29/Feb/2008:03:34:38 -0600] "HEAD /ply/ HTTP/1.1" 200 0 +80.58.205.45 - - [29/Feb/2008:03:36:03 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.249.147.120 - - [29/Feb/2008:03:36:33 -0600] "GET /ply/ HTTP/1.1" 200 8018 +70.249.147.120 - - [29/Feb/2008:03:36:35 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +70.249.147.120 - - [29/Feb/2008:03:36:37 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:38:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:40:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:03:47:00 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.10.60.85 - - [29/Feb/2008:03:48:58 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.1" 200 4154 +217.10.60.85 - - [29/Feb/2008:03:49:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLUsingCygwin HTTP/1.1" 200 2149 +194.2.41.91 - - [29/Feb/2008:03:56:43 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +194.2.41.91 - - [29/Feb/2008:03:56:50 -0600] "GET /cgi-bin/wiki.pl?SwigFaqDLLForWindows HTTP/1.0" 200 3589 +117.47.114.193 - - [29/Feb/2008:03:59:31 -0600] "GET /ply/ HTTP/1.1" 200 8018 +117.47.114.193 - - [29/Feb/2008:03:59:34 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +194.2.41.91 - - [29/Feb/2008:03:59:38 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +218.186.13.1 - - [29/Feb/2008:04:03:53 -0600] "GET /python/tutorial/beazley_advanced_python/Slides/SLIDE002.HTM HTTP/1.1" 200 1352 +218.186.13.1 - - [29/Feb/2008:04:03:54 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.196.43.134 - - [29/Feb/2008:04:05:51 -0600] "GET /ply/ HTTP/1.1" 200 8018 +66.232.113.62 - - [29/Feb/2008:04:06:28 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2733 +202.183.216.180 - - [29/Feb/2008:04:06:38 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.62.170.203 - - [29/Feb/2008:04:06:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +213.227.137.187 - - [29/Feb/2008:04:06:56 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.227.1.101 - - [29/Feb/2008:04:07:01 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +74.6.25.70 - - [29/Feb/2008:04:20:50 -0600] "GET /gifplot/index.html HTTP/1.0" 200 40215 +74.6.26.75 - - [29/Feb/2008:04:23:42 -0600] "GET /ply/ply.html HTTP/1.0" 200 97238 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/ HTTP/1.1" 200 8018 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12706 +193.206.186.101 - - [29/Feb/2008:04:30:40 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +193.206.186.101 - - [29/Feb/2008:04:30:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +193.206.186.101 - - [29/Feb/2008:04:32:37 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +74.6.19.72 - - [29/Feb/2008:04:39:31 -0600] "GET /dynamic/assign2.html HTTP/1.0" 200 4907 +74.6.23.12 - - [29/Feb/2008:04:43:13 -0600] "GET /dynamic/dowstocks.csv HTTP/1.0" 200 589814 +206.51.226.87 - - [29/Feb/2008:04:43:17 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2740 +218.64.214.110 - - [29/Feb/2008:04:43:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +200.195.243.236 - - [29/Feb/2008:04:43:40 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +196.217.249.190 - - [29/Feb/2008:04:43:43 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +203.199.177.121 - - [29/Feb/2008:04:43:47 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +80.97.94.178 - - [29/Feb/2008:04:43:54 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +87.18.28.170 - - [29/Feb/2008:04:44:16 -0600] "GET /ply/ HTTP/1.1" 200 8018 +41.232.219.113 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/ HTTP/1.1" 200 8018 +87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +87.18.28.170 - - [29/Feb/2008:04:44:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.219.113 - - [29/Feb/2008:04:44:18 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +41.232.219.113 - - [29/Feb/2008:04:44:19 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.217.150.45 - - [29/Feb/2008:04:46:29 -0600] "GET /ply/ HTTP/1.1" 200 8018 +129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +129.217.150.45 - - [29/Feb/2008:04:46:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +70.90.215.85 - - [29/Feb/2008:04:48:43 -0600] "GET /robots.txt HTTP/1.0" 200 71 +193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/ HTTP/1.0" 304 - +193.0.96.15 - - [29/Feb/2008:04:51:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 304 - +193.0.96.15 - - [29/Feb/2008:04:51:11 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "GET /cgi-bin/wiki.pl?SwigWiki HTTP/1.0" 200 2871 +84.110.221.201 - - [29/Feb/2008:04:52:22 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 200 1071 +81.80.245.157 - - [29/Feb/2008:04:53:04 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +81.80.245.157 - - [29/Feb/2008:04:54:37 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +84.165.112.79 - - [29/Feb/2008:04:55:52 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +84.165.112.79 - - [29/Feb/2008:04:55:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +86.125.158.237 - - [29/Feb/2008:04:58:13 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +74.6.7.146 - - [29/Feb/2008:04:58:16 -0600] "GET /swill/software.html HTTP/1.0" 404 133 +74.6.25.23 - - [29/Feb/2008:05:03:37 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE052.HTM HTTP/1.0" 200 1459 +74.6.20.166 - - [29/Feb/2008:05:11:50 -0600] "GET /ply/ply-2.1.tar.gz HTTP/1.0" 200 107720 +67.195.58.174 - - [29/Feb/2008:05:20:01 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.0" 200 115318 +74.6.26.209 - - [29/Feb/2008:05:23:06 -0600] "GET /python/consulting.html HTTP/1.0" 404 133 +68.37.149.27 - - [29/Feb/2008:05:24:38 -0600] "GET /ply/ HTTP/1.1" 200 8018 +89.165.73.226 - - [29/Feb/2008:05:32:57 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +89.165.73.226 - - [29/Feb/2008:05:33:01 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.226 - - [29/Feb/2008:05:33:02 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.165.73.226 - - [29/Feb/2008:05:33:05 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +91.103.40.50 - - [29/Feb/2008:05:42:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +62.96.202.33 - - [29/Feb/2008:05:45:14 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:41 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:48 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +62.96.202.33 - - [29/Feb/2008:05:45:53 -0600] "GET /cgi-bin/wiki.pl?WikiHowto HTTP/1.1" 200 3260 +210.197.158.144 - - [29/Feb/2008:05:56:36 -0600] "GET /ply/ HTTP/1.1" 200 8018 +217.127.12.71 - - [29/Feb/2008:06:10:12 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +217.127.12.71 - - [29/Feb/2008:06:10:13 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.127.12.71 - - [29/Feb/2008:06:10:24 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +217.127.12.71 - - [29/Feb/2008:06:11:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:15 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +125.238.252.69 - - [29/Feb/2008:06:16:16 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +201.236.226.90 - - [29/Feb/2008:06:20:12 -0600] "GET /dynamic/index.html HTTP/1.1" 304 - +82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +82.211.198.146 - - [29/Feb/2008:06:20:19 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +82.211.198.146 - - [29/Feb/2008:06:20:24 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs HTTP/1.1" 200 1601 +82.211.198.146 - - [29/Feb/2008:06:20:27 -0600] "GET /cgi-bin/wiki.pl?SwigWikiDocs/Introduction HTTP/1.1" 200 1939 +81.255.174.7 - - [29/Feb/2008:06:24:50 -0600] "GET /ply/ HTTP/1.0" 200 8018 +81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +81.255.174.7 - - [29/Feb/2008:06:24:51 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +62.160.169.7 - - [29/Feb/2008:06:24:59 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +81.255.174.7 - - [29/Feb/2008:06:24:59 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +217.128.46.55 - - [29/Feb/2008:06:27:07 -0600] "GET /cgi-bin/wiki.pl?SwigFaq/SharedLibraries HTTP/1.0" 200 4135 +217.128.46.55 - - [29/Feb/2008:06:27:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET /robots.txt HTTP/1.1" 200 71 +65.55.104.13 - - [29/Feb/2008:06:29:25 -0600] "GET / HTTP/1.1" 200 4447 +74.6.25.20 - - [29/Feb/2008:06:34:27 -0600] "GET /robots.txt HTTP/1.0" 200 71 +67.195.58.169 - - [29/Feb/2008:06:34:42 -0600] "GET /ply/ply-1.6.tar.gz HTTP/1.0" 200 72605 +212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +212.246.151.62 - - [29/Feb/2008:06:38:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +212.246.151.62 - - [29/Feb/2008:06:38:33 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.58.205.45 - - [29/Feb/2008:06:43:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +74.6.28.166 - - [29/Feb/2008:06:44:56 -0600] "GET /python/tutorial/beazley_intro_python/Slides/SLIDE106.HTM HTTP/1.0" 304 - +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +89.160.51.244 - - [29/Feb/2008:06:50:30 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /robots.txt HTTP/1.1" 200 71 +66.249.65.37 - - [29/Feb/2008:06:58:26 -0600] "GET /dynamic/03ProgramStructure.pdf HTTP/1.1" 304 - +200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/ HTTP/1.0" 200 8018 +200.19.92.10 - - [29/Feb/2008:06:58:30 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +200.19.92.10 - - [29/Feb/2008:06:58:31 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +131.111.113.139 - - [29/Feb/2008:07:03:26 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +131.111.113.139 - - [29/Feb/2008:07:03:40 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +190.198.190.239 - - [29/Feb/2008:07:04:59 -0600] "GET /ply/ HTTP/1.1" 200 8018 +190.198.190.239 - - [29/Feb/2008:07:05:02 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +66.249.65.37 - - [29/Feb/2008:07:05:18 -0600] "GET /dynamic/smackdown.py HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:06:23 -0600] "GET /dynamic/soln1.html HTTP/1.1" 304 - +80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /cgi-bin/wiki.pl HTTP/1.1" 200 2883 +80.120.2.52 - - [29/Feb/2008:07:09:17 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:07:11:32 -0600] "GET /dynamic/04Objects.pdf HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:13:19 -0600] "GET /dynamic/ HTTP/1.1" 200 5653 +66.249.65.37 - - [29/Feb/2008:07:14:41 -0600] "GET /dynamic/02WorkingWithData.pdf HTTP/1.1" 304 - +66.232.113.194 - - [29/Feb/2008:07:19:49 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +80.227.1.101 - - [29/Feb/2008:07:19:52 -0600] "POST /cgi-bin/wiki.pl HTTP/1.1" 302 183 +201.25.119.178 - - [29/Feb/2008:07:20:16 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +201.25.119.178 - - [29/Feb/2008:07:20:19 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +220.225.196.123 - - [29/Feb/2008:07:20:26 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +202.97.149.167 - - [29/Feb/2008:07:20:30 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +87.249.53.100 - - [29/Feb/2008:07:20:32 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +206.51.237.114 - - [29/Feb/2008:07:23:32 -0600] "GET /cgi-bin/wiki.pl?action=change1&id=ImportDirective HTTP/1.1" 200 2734 +123.190.193.8 - - [29/Feb/2008:07:23:36 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +194.177.202.247 - - [29/Feb/2008:07:23:45 -0600] "POST /cgi-bin/wiki.pl HTTP/1.0" 302 171 +66.249.65.37 - - [29/Feb/2008:07:23:52 -0600] "GET /dynamic/assign2.html HTTP/1.1" 304 - +66.249.65.37 - - [29/Feb/2008:07:25:09 -0600] "GET /python/python.html HTTP/1.1" 404 133 +66.249.65.37 - - [29/Feb/2008:07:27:52 -0600] "GET /dynamic/index.html HTTP/1.1" 200 5653 +66.249.65.37 - - [29/Feb/2008:07:27:56 -0600] "GET /dynamic/assign3.html HTTP/1.1" 304 - +200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /ply/ HTTP/1.0" 200 8018 +200.19.92.58 - - [29/Feb/2008:07:31:08 -0600] "GET /favicon.ico HTTP/1.0" 404 133 +200.19.92.58 - - [29/Feb/2008:07:31:09 -0600] "GET /ply/bookplug.gif HTTP/1.0" 200 23903 +66.249.65.37 - - [29/Feb/2008:07:31:24 -0600] "GET /dynamic/05ObjectModel.pdf HTTP/1.1" 304 - +81.222.64.10 - - [29/Feb/2008:07:31:44 -0600] "GET /ply/ HTTP/1.1" 200 8018 +74.6.26.11 - - [29/Feb/2008:07:31:48 -0600] "GET /photos/wind/pages/IMG_1321.htm HTTP/1.0" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:20 -0600] "GET /ply/ HTTP/1.1" 200 8018 +130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:21 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 12814 +130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +130.208.225.81 - - [29/Feb/2008:07:40:22 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +130.208.225.81 - - [29/Feb/2008:07:40:52 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +80.161.85.77 - - [29/Feb/2008:07:44:19 -0600] "GET /ply/ HTTP/1.1" 200 8018 +80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +80.161.85.77 - - [29/Feb/2008:07:44:20 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /ply/ply-2.3.tar.gz HTTP/1.1" 200 115318 +80.161.85.77 - - [29/Feb/2008:07:45:07 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:47:40 -0600] "GET /ply/example.html HTTP/1.1" 200 2359 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/ HTTP/1.1" 200 8018 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903 +156.63.68.202 - - [29/Feb/2008:07:49:28 -0600] "GET /favicon.ico HTTP/1.1" 404 133 +80.161.85.77 - - [29/Feb/2008:07:52:46 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238 diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0108.gz b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0108.gz new file mode 100644 index 0000000..54c786b Binary files /dev/null and b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0108.gz differ diff --git a/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0208.gz b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0208.gz new file mode 100644 index 0000000..d880029 Binary files /dev/null and b/chef/cookbooks/python/src/4/creating_data_processing_pipelines/www/foo/access-log-0208.gz differ diff --git a/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py b/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py new file mode 100644 index 0000000..b4e5cf7 --- /dev/null +++ b/chef/cookbooks/python/src/4/creating_new_iteration_patterns_with_generators/example.py @@ -0,0 +1,8 @@ +def frange(start, stop, increment): + x = start + while x < stop: + yield x + x += increment + +for n in frange(0, 4, 0.5): + print(n) diff --git a/chef/cookbooks/python/src/4/delegating-iteration/example.py b/chef/cookbooks/python/src/4/delegating-iteration/example.py new file mode 100644 index 0000000..ebbca8d --- /dev/null +++ b/chef/cookbooks/python/src/4/delegating-iteration/example.py @@ -0,0 +1,26 @@ +# Example of delegating iteration to an internal container + +class Node: + def __init__(self, value): + self._value = value + self._children = [] + + def __repr__(self): + return 'Node({!r})'.format(self._value) + + def add_child(self, node): + self._children.append(node) + + def __iter__(self): + return iter(self._children) + +# Example +if __name__ == '__main__': + root = Node(0) + child1 = Node(1) + child2 = Node(2) + root.add_child(child1) + root.add_child(child2) + for ch in root: + print(ch) + # Outputs: Node(1), Node(2) diff --git a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py new file mode 100644 index 0000000..df9f0ed --- /dev/null +++ b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/example.py @@ -0,0 +1,37 @@ +# example.py +# +# Example of depth-first search using a generator + +class Node: + def __init__(self, value): + self._value = value + self._children = [] + + def __repr__(self): + return 'Node({!r})'.format(self._value) + + def add_child(self, node): + self._children.append(node) + + def __iter__(self): + return iter(self._children) + + def depth_first(self): + yield self + for c in self: + yield from c.depth_first() + +# Example +if __name__ == '__main__': + root = Node(0) + child1 = Node(1) + child2 = Node(2) + root.add_child(child1) + root.add_child(child2) + child1.add_child(Node(3)) + child1.add_child(Node(4)) + child2.add_child(Node(5)) + + for ch in root.depth_first(): + print(ch) + # Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5) diff --git a/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py new file mode 100644 index 0000000..71de3d6 --- /dev/null +++ b/chef/cookbooks/python/src/4/easy_implementation_of_the_iterator_protocol/hardexample.py @@ -0,0 +1,66 @@ +# Hard example of depth-first iteration using an iterator object + +class Node: + def __init__(self, value): + self._value = value + self._children = [] + + def __repr__(self): + return 'Node(%r)' % self._value + + def add_child(self, other_node): + self._children.append(other_node) + + def __iter__(self): + return iter(self._children) + + def depth_first(self): + return DepthFirstIterator(self) + +class DepthFirstIterator(object): + ''' + Depth-first traversal + ''' + def __init__(self, start_node): + self._node = start_node + self._children_iter = None + self._child_iter = None + + def __iter__(self): + return self + + def __next__(self): + # Return myself if just started. Create an iterator for children + if self._children_iter is None: + self._children_iter = iter(self._node) + return self._node + + # If processing a child, return its next item + elif self._child_iter: + try: + nextchild = next(self._child_iter) + return nextchild + except StopIteration: + self._child_iter = None + return next(self) + + # Advance to the next child and start its iteration + else: + self._child_iter = next(self._children_iter).depth_first() + return next(self) + + +# Example +if __name__ == '__main__': + root = Node(0) + child1 = Node(1) + child2 = Node(2) + root.add_child(child1) + root.add_child(child2) + child1.add_child(Node(3)) + child1.add_child(Node(4)) + child2.add_child(Node(5)) + + for ch in root.depth_first(): + print(ch) + # Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5) diff --git a/chef/cookbooks/python/src/4/generators_with_state/example.py b/chef/cookbooks/python/src/4/generators_with_state/example.py new file mode 100644 index 0000000..32f55d8 --- /dev/null +++ b/chef/cookbooks/python/src/4/generators_with_state/example.py @@ -0,0 +1,29 @@ +# Example of a generator with extra state that can be +# accessed. Simply define as a class! + +from collections import deque + +class linehistory: + def __init__(self, lines, histlen=3): + self.lines = lines + self.history = deque(maxlen=histlen) + + def __iter__(self): + for lineno, line in enumerate(self.lines,1): + self.history.append((lineno, line)) + yield line + + def clear(self): + self.history.clear() + +with open('somefile.txt') as f: + lines = linehistory(f) + for line in lines: + if 'python' in line: + for lineno, hline in lines.history: + print('{}:{}'.format(lineno, hline), end='') + + + + + diff --git a/chef/cookbooks/python/src/4/generators_with_state/somefile.txt b/chef/cookbooks/python/src/4/generators_with_state/somefile.txt new file mode 100644 index 0000000..01841a2 --- /dev/null +++ b/chef/cookbooks/python/src/4/generators_with_state/somefile.txt @@ -0,0 +1,4 @@ +hello world +this is a test +of iterating over lines with a history +python is fun diff --git a/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py b/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py new file mode 100644 index 0000000..5f69236 --- /dev/null +++ b/chef/cookbooks/python/src/4/how_to_flatten_a_nested_sequence/example.py @@ -0,0 +1,20 @@ +# Example of flattening a nested sequence using subgenerators + +from collections import Iterable + +def flatten(items, ignore_types=(str, bytes)): + for x in items: + if isinstance(x, Iterable) and not isinstance(x, ignore_types): + yield from flatten(x) + else: + yield x + +items = [1, 2, [3, 4, [5, 6], 7], 8] + +# Produces 1 2 3 4 5 6 7 8 +for x in flatten(items): + print(x) + +items = ['Dave', 'Paula', ['Thomas', 'Lewis']] +for x in flatten(items): + print(x) diff --git a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py new file mode 100644 index 0000000..580a13b --- /dev/null +++ b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/example.py @@ -0,0 +1,11 @@ +# Example of iterating over lines of a file with an extra lineno attribute +def parse_data(filename): + with open(filename, 'rt') as f: + for lineno, line in enumerate(f, 1): + fields = line.split() + try: + count = int(fields[1]) + except ValueError as e: + print('Line {}: Parse error: {}'.format(lineno, e)) + +parse_data('sample.dat') diff --git a/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat new file mode 100644 index 0000000..493cc58 --- /dev/null +++ b/chef/cookbooks/python/src/4/iterate_over_the_index-value_pairs_of_a_list/sample.dat @@ -0,0 +1,6 @@ +0 1 +2 3 +4 N/A +5 6 +7 8 +9 10 diff --git a/chef/cookbooks/python/src/4/iterating_in_reverse/example.py b/chef/cookbooks/python/src/4/iterating_in_reverse/example.py new file mode 100644 index 0000000..f116252 --- /dev/null +++ b/chef/cookbooks/python/src/4/iterating_in_reverse/example.py @@ -0,0 +1,28 @@ +# Example of an object implementing both forward and reversed iterators + +class Countdown: + def __init__(self, start): + self.start = start + + # Forward iterator + def __iter__(self): + n = self.start + while n > 0: + yield n + n -= 1 + + # Reverse iterator + def __reversed__(self): + n = 1 + while n <= self.start: + yield n + n += 1 + +c = Countdown(5) +print("Forward:") +for x in c: + print(x) + +print("Reverse:") +for x in reversed(c): + print(x) diff --git a/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py b/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py new file mode 100644 index 0000000..642ace9 --- /dev/null +++ b/chef/cookbooks/python/src/4/iterating_in_sorted_order_over_merged_sorted_iterables/example.py @@ -0,0 +1,8 @@ +# Iterating over merged sorted iterables + +import heapq +a = [1, 4, 7, 10] +b = [2, 5, 6, 11] +for c in heapq.merge(a, b): + print(c) + diff --git a/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py b/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py new file mode 100644 index 0000000..43e4f1b --- /dev/null +++ b/chef/cookbooks/python/src/4/iterating_on_items_in_separate_containers/example.py @@ -0,0 +1,8 @@ +# Example of iterating over two sequences as one + +from itertools import chain +a = [1, 2, 3, 4] +b = ['x', 'y', 'z'] +for x in chain(a, b): + print(x) + diff --git a/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py b/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py new file mode 100644 index 0000000..fea4899 --- /dev/null +++ b/chef/cookbooks/python/src/5/adding_or_changing_the_encoding_of_an_already_open_file/example.py @@ -0,0 +1,11 @@ +# Example of adding a text encoding to existing file-like object + +import urllib.request +import io + +u = urllib.request.urlopen('http://www.python.org') +f = io.TextIOWrapper(u, encoding='utf-8') +text = f.read() + +print(text) + diff --git a/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py b/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py new file mode 100644 index 0000000..6f25844 --- /dev/null +++ b/chef/cookbooks/python/src/5/getting_a_directory_listing/example.py @@ -0,0 +1,19 @@ +# Example of getting a directory listing + +import os +import os.path +import glob + +pyfiles = glob.glob('*.py') + +# Get file sizes and modification dates +name_sz_date = [(name, os.path.getsize(name), os.path.getmtime(name)) + for name in pyfiles] + +for r in name_sz_date: + print(r) + +# Get file metadata +file_metadata = [(name, os.stat(name)) for name in pyfiles] +for name, meta in file_metadata: + print(name, meta.st_size, meta.st_mtime) diff --git a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin new file mode 100644 index 0000000..90d9316 --- /dev/null +++ b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/data.bin @@ -0,0 +1 @@ + 0 5412 N CLARK 3 5148 N CLARK 10 5800 E 58TH 4 2122 N CLARK 1 5645 N RAVENSWOOD 7 1060 W ADDISON 6 4801 N BROADWAY 1 1039 W GRANVILLE \ No newline at end of file diff --git a/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py new file mode 100644 index 0000000..0c543f2 --- /dev/null +++ b/chef/cookbooks/python/src/5/iterating_over_fixed-sized_records/example.py @@ -0,0 +1,13 @@ +# Example of iterating of fixed-size records +# +# The file 'data.bin' contains 32-byte fixed size records +# that consist of a 4-digit number followed by a 28-byte string. + +from functools import partial +RECORD_SIZE = 32 + +with open('data.bin', 'rb') as f: + records = iter(partial(f.read, RECORD_SIZE), b'') + for r in records: + print(r) + diff --git a/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py b/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py new file mode 100644 index 0000000..98f3ada --- /dev/null +++ b/chef/cookbooks/python/src/5/reading_and_writing_text_data/example.py @@ -0,0 +1,30 @@ +# Some examples of reading text files with different options +# +# The file sample.txt is a UTF-8 encoded text file with Windows +# line-endings (\r\n). + +# (a) Reading a basic text file (UTF-8 default encoding) + +print("Reading a simple text file (UTF-8)") +with open('sample.txt', 'rt') as f: + for line in f: + print(repr(line)) + +# (b) Reading a text file with universal newlines turned off +print("Reading text file with universal newlines off") +with open('sample.txt', 'rt', newline='') as f: + for line in f: + print(repr(line)) + +# (c) Reading text file as ASCII with replacement error handling +print("Reading text as ASCII with replacement error handling") +with open('sample.txt', 'rt', encoding='ascii', errors='replace') as f: + for line in f: + print(repr(line)) + +# (d) Reading text file as ASCII with ignore error handling +print("Reading text as ASCII with ignore error handling") +with open('sample.txt', 'rt', encoding='ascii', errors='ignore') as f: + for line in f: + print(repr(line)) + diff --git a/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt b/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt new file mode 100644 index 0000000..3fe0544 --- /dev/null +++ b/chef/cookbooks/python/src/5/reading_and_writing_text_data/sample.txt @@ -0,0 +1,2 @@ +Hello World +Spicy Jalapeño diff --git a/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py b/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py new file mode 100644 index 0000000..c68fb09 --- /dev/null +++ b/chef/cookbooks/python/src/5/wrapping_an_existing_file_descriptor_as_a_file_object/echo.py @@ -0,0 +1,26 @@ +from socket import socket, AF_INET, SOCK_STREAM + +def echo_client(client_sock, addr): + print("Got connection from", addr) + + # Make text-mode file wrappers for socket reading/writing + client_in = open(client_sock.fileno(), 'rt', encoding='latin-1', closefd=False) + client_out = open(client_sock.fileno(), 'wt', encoding='latin-1', closefd=False) + + # Echo lines back to the client using file I/O + for line in client_in: + client_out.write(line) + client_out.flush() + client_sock.close() + +def echo_server(address): + sock = socket(AF_INET, SOCK_STREAM) + sock.bind(address) + sock.listen(1) + while True: + client, addr = sock.accept() + echo_client(client, addr) + +if __name__ == '__main__': + print('Echo serving running on localhost:25000') + echo_server(('', 25000)) diff --git a/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py b/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py new file mode 100644 index 0000000..45274ae --- /dev/null +++ b/chef/cookbooks/python/src/5/writing_bytes_to_a_text_file/example.py @@ -0,0 +1,9 @@ +# Example of writing raw bytes on a file opened in text mode + +import sys + +# A byte string +data = b'Hello World\n' + +# Write onto the buffer attribute (bypassing text encoding) +sys.stdout.buffer.write(data) diff --git a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py new file mode 100644 index 0000000..428b192 --- /dev/null +++ b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/example.py @@ -0,0 +1,42 @@ +# Example of incremental XML parsing +# +# The file 'potholes.xml' is a greatly condensed version of a larger +# file available for download at +# +# https://data.cityofchicago.org/api/views/7as2-ds3y/rows.xml?accessType=DOWNLOAD + +from xml.etree.ElementTree import iterparse + +def parse_and_remove(filename, path): + path_parts = path.split('/') + doc = iterparse(filename, ('start', 'end')) + # Skip the root element + next(doc) + + tag_stack = [] + elem_stack = [] + for event, elem in doc: + if event == 'start': + tag_stack.append(elem.tag) + elem_stack.append(elem) + elif event == 'end': + if tag_stack == path_parts: + yield elem + elem_stack[-2].remove(elem) + try: + tag_stack.pop() + elem_stack.pop() + except IndexError: + pass + +# Find zip code with most potholes + +from collections import Counter +potholes_by_zip = Counter() + +data = parse_and_remove('potholes.xml', 'row/row') +for pothole in data: + potholes_by_zip[pothole.findtext('zip')] += 1 + +for zipcode, num in potholes_by_zip.most_common(): + print(zipcode, num) diff --git a/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml new file mode 100644 index 0000000..50cff26 --- /dev/null +++ b/chef/cookbooks/python/src/6/incremental_parsing_of_huge_xml_files/potholes.xml @@ -0,0 +1 @@ +2012-12-19T00:00:00Open12-02020904Pot Hole in Street2920 W BERTEAU AVE606181156066.361768711927765.9484328233171641.957581829809655-87.701641097511192012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019619Pot Hole in StreetFinal OutcomePothole Patched43100 E 91ST ST606171198477.930077221845218.041123861044641.7301045945226-87.548498614855972012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019580Pot Hole in StreetFinal OutcomePothole Patched27955 S YATES BLVD606171193574.97740621852682.85992567744641.7507099762241-87.566215496320072012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019611Pot Hole in StreetFinal OutcomePothole Patched32432 E 85TH ST606171193978.129408951849090.7837193744641.74084314846648-87.564855740304172012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020737Pot Hole in StreetFinal OutcomeCDOT Street Cut Complaints Transfer Outcome1045 N LEAVITT ST606221161546.433317581907023.3319016532132441.90055013339103-87.682073769876582012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019763Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome2920 N WOLCOTT AVE606571163167.176581291919489.616577563219541.93472453511495-87.675769614882972012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019689Pot Hole in StreetFinal OutcomePothole Patched1879 E 87TH ST606191183832.149967161847487.15981401864441.736685241835445-87.602079173420082012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019673Pot Hole in StreetFinal OutcomePothole Patched1113 E 89TH ST606191178769.99988161846008.70128963664441.73274470299228-87.620669965886962012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020693Pot Hole in StreetFinal OutcomeCDOT Street Cut Complaints Transfer Outcome3939 N GREENVIEW AVE606131165370.643006821926241.561024174719641.95320554373681-87.667479116856472012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019897Pot Hole in StreetFinal OutcomePothole Patched35146 W MADISON ST606441142015.870007911899534.9240188728152541.88038548666047-87.753996959426392012-12-19T00:00:00Completed2012-12-19T00:00:0012-02020428Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome729 W OHIO ST606541171188.825614231904077.4996863527132441.89226026539505-87.646743537803082012-12-19T00:00:00Open12-02016542Pot Hole in Street6733 N RAVENSWOOD AVE606261163128.210831711944874.41000854924142.00438228733084-87.675196177342162012-12-19T00:00:00Open12-02016538Pot Hole in Street1757 W WALLEN AVE606261163421.962229821944218.250110524024142.002575567056205-87.674134048781872012-12-19T00:00:00Open12-02018384Pot Hole in Street5832 N WAYNE AVE606601166416.780030511938817.4000296548207741.98769171278258-87.663271883457142012-12-19T00:00:00Open12-02017827Pot Hole in Street1601 W 108TH PL606431167584.129828971832775.5134824319227541.69667762299553-87.662026852236352012-12-19T00:00:00Open12-02016893Pot Hole in Street5739 W IRVING PARK RD606341137257.650074861926013.1959721538161541.95313174383805-87.770831231016232012-12-19T00:00:00Open12-02018381Pot Hole in Street5611 S ELIZABETH ST606361168947.618451867520.6816751676741.79199392572382-87.656033246291732012-12-19T00:00:00Open12-02017861Pot Hole in Street3100 W MOFFAT ST606471155214.690690931912130.789971526142241.91469498906821-87.70519328561312012-12-19T00:00:00Open12-02017942Pot Hole in Street1600 N HAMLIN AVE606471150804.150121361910366.0473089630252341.90993986745698-87.72144336821242012-12-19T00:00:00Open12-02017890Pot Hole in Street3000 W WABANSIA AVE606471156017.528910481911152.9897076426142341.91199565103413-87.702270187839022012-12-19T00:00:00Open12-02020051Pot Hole in Street4900 S KEDVALE AVE606321149522.712482521871811.643677752385741.80416684102511-87.727150814610172012-12-19T00:00:00Open12-02018862Pot Hole in Street1932 N KARLOV AVE606391148741.744007131912523.708419730252041.91590082226891-87.728964013987122012-12-19T00:00:00Open12-02018812Pot Hole in Street4967 N KOLMAR AVE606301144492.799490361932611.0693273639171441.97110345287727-87.744067178315122012-12-19T00:00:00Open12-02019299Pot Hole in Street634 W BUENA AVE606131171069.607805941928227.029870574619341.958530321478996-87.646470932735872012-12-19T00:00:00Open12-02018897Pot Hole in Street2514 W FOSTER AVE606251158477.848807461934446.829913884020441.97586539743432-87.692591987383822012-12-19T00:00:00Open12-02018977Pot Hole in Street5561 N CAMPBELL AVE606251158662.175603251936854.696367994020441.98246890846856-87.691847835569082012-12-19T00:00:00Open12-02018907Pot Hole in Street9500 S STONY ISLAND AVE606171188504.434302771842299.31671488855141.72233904922372-87.585126874465392012-12-19T00:00:00Open12-02020260Pot Hole in Street3700 N PINE GROVE AVE606131171046.075995151925024.455267124619641.94974286922904-87.646651783685942012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017879Pot Hole in StreetFinal OutcomePothole Patched239800 S PARNELL AVE606281174361.22248291839902.9701241421227341.71608877775062-87.637002185453452012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019438Pot Hole in StreetFinal OutcomePothole Patched12300 S TROY ST606231155719.367228041888546.8520800424103041.84996817801136-87.703974573182552012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018921Pot Hole in StreetFinal OutcomePothole Patched282700 E 81ST ST606171195580.718124521851783.73762544744641.748193407358585-87.55889531375412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018900Pot Hole in StreetFinal OutcomePothole Patched74846 S WOLCOTT AVE606091164503.20981471872542.118332252096141.80586828661782-87.672188548470092012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018451Pot Hole in StreetFinal OutcomePothole Patched15400 N CLAREMONT AVE606121160670.990029981902655.1801259127132441.8885817539312-87.685410481665672012-12-19T00:00:00Open12-02020574Pot Hole in Street2432 S NB LSD OB STEVENSON ER606161180671.865912361888412.47283406213341.84906109948375-87.612399963070962012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018351Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome2523 W MARQUETTE RD606291160614.628816371860143.032324791586641.77192472731556-87.68679243658212012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017916Pot Hole in StreetFinal OutcomePothole Patched89800 S LOWE AVE606281173699.674325471839888.4627214621227341.716063624039734-87.639425532132482012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018523Pot Hole in StreetFinal OutcomePothole Patched102600 N RACINE AVE606141167861.483594151917411.986392253219741.92892331669152-87.658578137042272012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018867Pot Hole in StreetFinal OutcomePothole Patched184850 S WOOD ST606091165156.790717451872863.873075212096141.80673739778276-87.66978234464462012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018949Pot Hole in StreetFinal OutcomePothole Patched22650 E 75TH ST606491195304.849483621855816.62177611734341.75926675632701-87.559773296799722012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018953Pot Hole in StreetFinal OutcomePothole Patched202901 E 82ND ST606171196930.818209271851157.41767415744641.746441286589324-87.553968993024012012-12-19T00:00:00Open12-02018663Pot Hole in Street8600 S ESCANABA AVE606171196975.311603061848507.592496371044641.73916885584215-87.553893907909332012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018922Pot Hole in StreetFinal OutcomePothole Patched158100 S MANISTEE AVE606171195911.029523481851792.62908157744641.74820964166295-87.557684674779212012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019450Pot Hole in StreetFinal OutcomePothole Patched73240 W 23RD ST606231155023.809111451888530.476505922103041.84993720174623-87.706527830931832012-12-19T00:00:00Open12-02020655Pot Hole in Street4700 N LEAVITT ST606251160841.911505311931178.761984084719441.966848819565755-87.68398952242462012-12-19T00:00:00Open12-02016521Pot Hole in Street1738 W ALBION AVE606261163600.381885511943891.479477394024142.0016751289695-87.673486924070252012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019461Pot Hole in StreetFinal OutcomePothole Patched42252 S SPAULDING AVE606231154715.840027621888841.0699707422103041.850795669247546-87.707649825027432012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018943Pot Hole in StreetFinal OutcomePothole Patched697500 S COLES AVE606491195618.733434211856037.07646802744341.75986394886502-87.558615671729112012-12-19T00:00:00Open12-02020182Pot Hole in Street2937 N KILPATRICK AVE606411144538.435867811919116.5525243931251941.93407248160905-87.744240446607142012-12-19T00:00:00Open12-02020379Pot Hole in Street3600 W 86TH ST606521153829.165088231847310.569993351887041.736847628628695-87.712005771594732012-12-19T00:00:00Open12-02016541Pot Hole in Street6723 N RAVENSWOOD AVE606261163130.910831711944784.51000854924142.004135542542144-87.675188786476712012-12-19T00:00:00Open12-02016714Pot Hole in Street6800 W DICKENS AVE607071130743.647000131913216.180002736252541.91813004984083-87.795072980484482012-12-19T00:00:00Completed2012-12-19T00:00:0012-02018886Pot Hole in StreetFinal OutcomePothole Patched14931 S WOLCOTT AVE606091164519.226469771871953.963776681696141.80425398259015-87.67214639971472012-12-19T00:00:00Open12-02020903Pot Hole in Street4201 N SACRAMENTO AVE606181155630.228005161927768.1339811333171641.957596637909006-87.703244414243762012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019614Pot Hole in StreetFinal OutcomePothole Patched42900 E 85TH ST606171196961.347305751849170.485810921044641.74098823420314-87.553923073057622012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019318Pot Hole in StreetFinal OutcomeGAS Peoples Gas Transfer Outcome04812 N WESTERN AVE606251159495.937051931954.017154720441.96900404348566-87.688917043899022012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019667Pot Hole in StreetFinal OutcomePothole Patched175 E 89TH ST606191178522.49988161846000.50128963664441.732727821186984-87.621576913168412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019539Pot Hole in StreetFinal OutcomePothole Patched472216 S CHRISTIANA AVE606231154374.522970681889069.4257695722103041.851429121666584-87.708896440135332012-12-19T00:00:00Open12-02016533Pot Hole in Street1727 W WALLEN AVE606261163618.009573751944224.556016474024142.002588726496306-87.673412629962682012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019551Pot Hole in StreetFinal OutcomePothole Patched263299 W 23RD ST606231154727.740027621888531.2699707422103041.84994530413951-87.707614434444762012-12-19T00:00:00Open12-02016505Pot Hole in Street6709 N RAVENSWOOD AVE606261163135.159676811944636.209994114924142.00372851396752-87.67517734909632012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019333Pot Hole in StreetFinal OutcomePothole Patched391900 W 56TH ST606361164643.077510331867534.738994171576741.79212447623052-87.671816899249412012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019314Pot Hole in StreetFinal OutcomePothole Patched272500 W MARQUETTE RD606291160863.126714041860150.359428591586641.77193970401319-87.685881313787492012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019288Pot Hole in StreetFinal OutcomeNo Potholes Found0600 N UNION AVE606541171402.85882131904217.0849169827132441.89263859350436-87.645953383388362012-12-19T00:00:00Open12-02016535Pot Hole in Street1734 W WALLEN AVE606261163572.209573751944223.056016474924142.00258557900276-87.673581166473232012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019339Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome650 W BUENA AVE606131171006.807805941928225.129870574619341.95852648885717-87.646701863647042012-12-19T00:00:00Completed2012-12-19T00:00:0012-02019215Pot Hole in StreetFinal OutcomePothole Patched281011 S AUSTIN BLVD606441136525.838472871895323.1099261129152541.86892761647423-87.774256847137012012-12-19T00:00:00Open12-02020315Pot Hole in Street6500 W BRYN MAWR AVE606311131870.811919781936822.0363017341161041.98288759839332-87.790382187702892012-12-19T00:00:00Completed2012-12-19T00:00:0012-02017865Pot Hole in StreetFinal OutcomePothole Patched55399 W WARNER AVE606411139607.3389031927064.680002838161541.95597446983892-87.762167718529472012-12-18T00:00:00Open12-02015927Pot Hole in Street2300 E 89TH ST606171193050.075296681846409.73700587844841.733508808623625-87.568343302097692012-12-18T00:00:00Open12-02016049Pot Hole in Street6200 S SPRINGFIELD AVE606291151435.905601031863206.10228361386541.78051469899573-87.720359079303492012-12-18T00:00:00Open12-02014821Pot Hole in Street10100 S LOWE AVE606281173753.184877911837902.3415178134227341.71061223782308-87.639288184652412012-12-18T00:00:00Open12-02014829Pot Hole in Street10050 S PARNELL AVE606281174396.554091521838582.828098619227341.71246533613287-87.636911891281512012-12-18T00:00:00Open12-02015911Pot Hole in Street8743 S CORNELL AVE606171188767.339885591847238.24499717844841.73588570326689-87.584006416345992012-12-18T00:00:00Open12-02015921Pot Hole in Street1640 E 88TH ST606171188820.351914971846958.7811206844841.735117559761015-87.583821118987972012-12-18T00:00:00Open12-02011376Pot Hole in Street2400 S KEELER AVE606231148769.329986821887578.649984422103041.847448249396926-87.729507482569322012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012781Pot Hole in StreetFinal OutcomePothole Patched22047 N KIMBALL AVE606471153376.144988391913496.1300875335142241.91847835197719-87.711911540612952012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013038Pot Hole in StreetFinal OutcomePothole Patched53517 W HIRSCH ST606511152660.557413871909086.5946263126142341.90639238848314-87.71465754470772012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012767Pot Hole in StreetFinal OutcomePothole Patched122025 N SPAULDING AVE606471153820.938814681913260.2759227335142241.91782229222199-87.710283619170242012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012799Pot Hole in StreetFinal OutcomeNo Potholes Found01306 N KEDZIE AVE606511154789.017428651908532.7498680426142341.9048301975562-87.706853705952812012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012793Pot Hole in StreetFinal OutcomePothole Patched72014 N KEDZIE AVE606471154719.202980981913269.686198635142241.91783015979474-87.706983089911472012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013422Pot Hole in StreetFinal OutcomeNo Potholes Found01300 N RIDGEWAY AVE606511151136.649992491908396.2502657426252341.904528041033295-87.720273608888082012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012703Pot Hole in StreetFinal OutcomePothole Patched102350 W 66TH ST606361161779.702725481860829.594685531586641.77378463656647-87.682502573007452012-12-18T00:00:00Completed2012-12-19T00:00:0012-02011828Pot Hole in StreetFinal OutcomePothole Patched104934 W BELLE PLAINE AVE606411142625.125208961926800.1816916845161541.95519293466957-87.751080091429032012-12-18T00:00:00Completed2012-12-19T00:00:0012-02011990Pot Hole in StreetFinal OutcomePothole Patched147045 W 63RD PL606381130526.933317781861618.680083012386441.776541941755795-87.797053042389662012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012946Pot Hole in StreetFinal OutcomePothole Patched13555 W LE MOYNE ST606511152383.73850931909738.9377678426142341.90818795288391-87.715657158490972012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013057Pot Hole in StreetFinal OutcomePothole Patched23555 W HIRSCH ST606511152405.20051631909081.6442083626142341.90638385412921-87.715595706278182012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012647Pot Hole in StreetFinal OutcomeCDOT Pavement Cave-In Survey Transfer Outcome06728 S MARSHFIELD AVE606361166496.597510371860091.892007431576741.77166102840039-87.665232380471882012-12-18T00:00:00Completed2012-12-18T00:00:0012-02015136Pot Hole in StreetFinal OutcomePothole Patched211100 S AVENUE J606171202538.004899431831972.930300591045241.6936564167099-87.53407609309342012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013356Pot Hole in StreetFinal OutcomePothole Patched61535 N LAWNDALE AVE606511151483.580199071909955.3056113126252341.908799426285036-87.718958210476832012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013347Pot Hole in StreetFinal OutcomeNo Potholes Found02919 W DIVISION ST606221156641.350105591907841.885094426132441.90289708164128-87.700068245626852012-12-18T00:00:00Completed2012-12-19T00:00:0012-02012834Pot Hole in StreetFinal OutcomeNo Potholes Found03201 W LE MOYNE ST606511154743.831371691909792.2999952326142341.90828742043949-87.706985904904042012-12-18T00:00:00Open12-02016327Pot Hole in Street1948 W HENDERSON ST606571162762.312910441922254.030120073219541.942318756367634-87.677179732082252012-12-18T00:00:00Completed2012-12-18T00:00:0012-02015091Pot Hole in StreetFinal OutcomePothole Patched92850 N ASHLAND AVE606571165165.035675121919079.091499753219641.93355574831535-87.668439176802042012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013330Pot Hole in StreetFinal OutcomePothole Patched55813 S NOTTINGHAM AVE606381130038.055081511865180.835327462385641.78632553696458-87.79876410815342012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014737Pot Hole in StreetFinal OutcomePothole Patched24560 N FORESTVIEW AVE606561116892.730089761929173.2899099636167641.96214582973867-87.84562986179722012-12-18T00:00:00Completed2012-12-19T00:00:0012-02013331Pot Hole in StreetFinal OutcomePothole Patched101220 N HUMBOLDT DR606221156061.657875541908054.7095913926142441.903492813736975-87.702191819695492012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014725Pot Hole in StreetFinal OutcomePothole Patched28729 W LELAND AVE606561117080.109967611930271.6832908436167641.96515703075439-87.844917904276162012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014629Pot Hole in StreetFinal OutcomePothole Patched66505 N ASHLAND AVE606261164432.099473831943294.236827754024142.0000186516124-87.670444167290362012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014591Pot Hole in StreetFinal OutcomePothole Patched154700 S PAULINA ST606091165802.342478951873540.894569322096141.80858152134749-87.667395428440782012-12-18T00:00:00Completed2012-12-18T00:00:0012-02014626Pot Hole in StreetFinal OutcomePothole Patched311500 E 68TH ST606371187397.750120371860201.13658902534341.7714897122845-87.5886129339915 \ No newline at end of file diff --git a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py new file mode 100644 index 0000000..cd43c22 --- /dev/null +++ b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/example.py @@ -0,0 +1,21 @@ +# example.py +# +# Example of reading an XML document, making changes, and writing it back out + +from xml.etree.ElementTree import parse, Element +doc = parse('pred.xml') +root = doc.getroot() + +# Remove a few elements +root.remove(root.find('sri')) +root.remove(root.find('cr')) + +# Insert a new element after ... +nm_index = root.getchildren().index(root.find('nm')) + +e = Element('spam') +e.text = 'This is a test' +root.insert(nm_index + 1, e) + +# Write back to a file +doc.write('newpred.xml', xml_declaration=True) diff --git a/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml new file mode 100644 index 0000000..b460dec --- /dev/null +++ b/chef/cookbooks/python/src/6/parsing_modifying_and_rewriting_xml/pred.xml @@ -0,0 +1,23 @@ + + + 14791 + Clark & Balmoral + + 22 + North Bound +
North Bound
+
+ 22 +
+       5 MIN
+       Howard
+       1378
+       22
+   
+
+       15 MIN
+       Howard
+       1867
+       22
+   
+
diff --git a/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py b/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py new file mode 100644 index 0000000..148f08d --- /dev/null +++ b/chef/cookbooks/python/src/6/parsing_simple_xml_data/example.py @@ -0,0 +1,17 @@ +from urllib.request import urlopen +from xml.etree.ElementTree import parse + +# Download the RSS feed and parse it +u = urlopen('http://planet.python.org/rss20.xml') +doc = parse(u) + +# Extract and output tags of interest +for item in doc.iterfind('channel/item'): + title = item.findtext('title') + date = item.findtext('pubDate') + link = item.findtext('link') + + print(title) + print(date) + print(link) + print() diff --git a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py new file mode 100644 index 0000000..946c565 --- /dev/null +++ b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/example.py @@ -0,0 +1,26 @@ +# example.py +# +# Example of XML namespace handling + +from xml.etree.ElementTree import parse + +class XMLNamespaces: + def __init__(self, **kwargs): + self.namespaces = {} + for name, uri in kwargs.items(): + self.register(name, uri) + def register(self, name, uri): + self.namespaces[name] = '{'+uri+'}' + def __call__(self, path): + return path.format_map(self.namespaces) + +doc = parse('sample.xml') +ns = XMLNamespaces(html='http://www.w3.org/1999/xhtml') + +e = doc.find(ns('content/{html}html')) +print(e) + +text = doc.findtext(ns('content/{html}html/{html}head/{html}title')) +print(text) + + diff --git a/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml new file mode 100644 index 0000000..89d0b95 --- /dev/null +++ b/chef/cookbooks/python/src/6/parsing_xml_documents_with_namespaces/sample.xml @@ -0,0 +1,14 @@ + + + David Beazley + + + + Hello World + + +

Hello World!

+ + +
+
diff --git a/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py b/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py new file mode 100644 index 0000000..4819e39 --- /dev/null +++ b/chef/cookbooks/python/src/6/reading_and_writing_binary_arrays_of_structures/readrecords.py @@ -0,0 +1,14 @@ +from struct import Struct + +def read_records(format, f): + record_struct = Struct(format) + chunks = iter(lambda: f.read(record_struct.size), b'') + return (record_struct.unpack(chunk) for chunk in chunks) + +# Example +if __name__ == '__main__': + with open('data.b','rb') as f: + for rec in read_records('','!','@')): + byte_order = format[0] + format = format[1:] + format = byte_order + format + setattr(self, fieldname, StructField(format, offset)) + offset += struct.calcsize(format) + setattr(self, 'struct_size', offset) + +class Structure(metaclass=StructureMeta): + def __init__(self, bytedata): + self._buffer = memoryview(bytedata) + + @classmethod + def from_file(cls, f): + return cls(f.read(cls.struct_size)) + +if __name__ == '__main__': + class PolyHeader(Structure): + _fields_ = [ + ('','!','@')): + byte_order = format[0] + format = format[1:] + format = byte_order + format + setattr(self, fieldname, StructField(format, offset)) + offset += struct.calcsize(format) + setattr(self, 'struct_size', offset) + +class Structure(metaclass=StructureMeta): + def __init__(self, bytedata): + self._buffer = memoryview(bytedata) + + @classmethod + def from_file(cls, f): + return cls(f.read(cls.struct_size)) + +if __name__ == '__main__': + class Point(Structure): + _fields_ = [ + ('','!','@')): + byte_order = format[0] + format = format[1:] + format = byte_order + format + setattr(self, fieldname, StructField(format, offset)) + offset += struct.calcsize(format) + setattr(self, 'struct_size', offset) + +class Structure(metaclass=StructureMeta): + def __init__(self, bytedata): + self._buffer = memoryview(bytedata) + + @classmethod + def from_file(cls, f): + return cls(f.read(cls.struct_size)) + +class SizedRecord: + def __init__(self, bytedata): + self._buffer = memoryview(bytedata) + + @classmethod + def from_file(cls, f, size_fmt, includes_size=True): + sz_nbytes = struct.calcsize(size_fmt) + sz_bytes = f.read(sz_nbytes) + sz, = struct.unpack(size_fmt, sz_bytes) + buf = f.read(sz - includes_size * sz_nbytes) + return cls(buf) + + def iter_as(self, code): + if isinstance(code, str): + s = struct.Struct(code) + for off in range(0, len(self._buffer), s.size): + yield s.unpack_from(self._buffer, off) + elif isinstance(code, StructureMeta): + size = code.struct_size + for off in range(0, len(self._buffer), size): + data = self._buffer[off:off+size] + yield code(data) + +if __name__ == '__main__': + class Point(Structure): + _fields_ = [ + ('Albatross' +print(make_element('item', 'Albatross', size='large', quantity=6)) +print(make_element('p','')) diff --git a/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py b/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py new file mode 100644 index 0000000..b649fd3 --- /dev/null +++ b/chef/cookbooks/python/src/7/functions_that_only_accept_keyword_arguments/example.py @@ -0,0 +1,21 @@ +# examples of keyword-only argument functions + +# A simple keyword-only argument +def recv(maxsize, *, block=True): + print(maxsize, block) + +recv(8192, block=False) # Works +try: + recv(8192, False) # Fails +except TypeError as e: + print(e) + +# Adding keyword-only args to *args functions +def minimum(*values, clip=None): + m = min(values) + if clip is not None: + m = clip if clip > m else m + return m + +print(minimum(1, 5, 2, -5, 10)) +print(minimum(1, 5, 2, -5, 10, clip=0)) diff --git a/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py b/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py new file mode 100644 index 0000000..4ec1df7 --- /dev/null +++ b/chef/cookbooks/python/src/7/functions_with_default_arguments/example.py @@ -0,0 +1,42 @@ +# Examples of a function with default arguments + +# (a) Dangers of using a mutable default argument + +def spam(b=[]): + return b + +a = spam() +print(a) +a.append(1) +a.append(2) +b = spam() +print(b) # Carefully observe result +print('-'*10) + +# (b) Better alternative for mutable defaults +def spam(b=None): + if b is None: + b = [] + return b + +a = spam() +print(a) +a.append(1) +a.append(2) +b = spam() +print(b) +print('-'*10) + +# (c) Example of testing if an argument was supplied or not + +_no_value = object() +def spam(b=_no_value): + if b is _no_value: + print("No b value supplied") + else: + print("b=", b) + +spam() +spam(None) +spam(0) +spam([]) diff --git a/chef/cookbooks/python/src/7/inlining_callback_functions/example.py b/chef/cookbooks/python/src/7/inlining_callback_functions/example.py new file mode 100644 index 0000000..330836d --- /dev/null +++ b/chef/cookbooks/python/src/7/inlining_callback_functions/example.py @@ -0,0 +1,61 @@ +# Example of implementing an inlined-callback function + +# Sample function to illustrate callback control flow + +def apply_async(func, args, *, callback): + # Compute the result + result = func(*args) + + # Invoke the callback with the result + callback(result) + +# Inlined callback implementation +from queue import Queue +from functools import wraps + +class Async: + def __init__(self, func, args): + self.func = func + self.args = args + +def inlined_async(func): + @wraps(func) + def wrapper(*args): + f = func(*args) + result_queue = Queue() + result_queue.put(None) + while True: + result = result_queue.get() + try: + a = f.send(result) + apply_async(a.func, a.args, callback=result_queue.put) + except StopIteration: + break + return wrapper + +# Sample use +def add(x, y): + return x + y + +@inlined_async +def test(): + r = yield Async(add, (2, 3)) + print(r) + r = yield Async(add, ('hello', 'world')) + print(r) + for n in range(10): + r = yield Async(add, (n, n)) + print(r) + print('Goodbye') + +if __name__ == '__main__': + # Simple test + print('# --- Simple test') + test() + + print('# --- Multiprocessing test') + import multiprocessing + pool = multiprocessing.Pool() + apply_async = pool.apply_async + test() + diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py new file mode 100644 index 0000000..85f6517 --- /dev/null +++ b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example1.py @@ -0,0 +1,13 @@ +# Example of using partial() with sorting a list of (x,y) coordinates + +points = [ (1, 2), (3, 4), (5, 6), (7, 7) ] + +import math +def distance(p1, p2): + x1, y1 = p1 + x2, y2 = p2 + return math.hypot(x2 - x1, y2 - y1) + +pt = (4,3) +points.sort(key=partial(distance, pt)) +print(points) diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py new file mode 100644 index 0000000..bfe4f0e --- /dev/null +++ b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example2.py @@ -0,0 +1,22 @@ +# Using partial to supply extra arguments to a callback function + +def output_result(result, log=None): + if log is not None: + log.debug('Got: %r', result) + +# A sample function +def add(x, y): + return x + y + +if __name__ == '__main__': + import logging + from multiprocessing import Pool + from functools import partial + + logging.basicConfig(level=logging.DEBUG) + log = logging.getLogger('test') + + p = Pool() + p.apply_async(add, (3, 4), callback=partial(output_result, log=log)) + p.close() + p.join() diff --git a/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py new file mode 100644 index 0000000..5e6b31c --- /dev/null +++ b/chef/cookbooks/python/src/7/making_an_n-argument_callable_work_as_a_callable_with_fewer_arguments/example3.py @@ -0,0 +1,18 @@ +# Using partial to supply extra arguments to a class constructor +from socketserver import StreamRequestHandler, TCPServer + +class EchoHandler(StreamRequestHandler): + # ack is added keyword-only argument. *args, **kwargs are + # any normal parameters supplied (which are passed on) + def __init__(self, *args, ack, **kwargs): + self.ack = ack + super().__init__(*args, **kwargs) + def handle(self): + for line in self.rfile: + self.wfile.write(self.ack + line) + +if __name__ == '__main__': + from functools import partial + serv = TCPServer(('', 15000), partial(EchoHandler, ack=b'RECEIVED:')) + print('Echo server running on port 15000') + serv.serve_forever() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py new file mode 100644 index 0000000..d029e62 --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example1.py @@ -0,0 +1,12 @@ +class A: + def spam(self): + print('A.spam') + +class B(A): + def spam(self): + print('B.spam') + super().spam() # Call parent spam() + +if __name__ == '__main__': + b = B() + b.spam() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py new file mode 100644 index 0000000..275dac2 --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example2.py @@ -0,0 +1,12 @@ +class A: + def __init__(self): + self.x = 0 + +class B(A): + def __init__(self): + super().__init__() + self.y = 1 + +if __name__ == '__main__': + b = B() + print(b.x, b.y) diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py new file mode 100644 index 0000000..29de53a --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example3.py @@ -0,0 +1,31 @@ +class Proxy: + def __init__(self, obj): + self._obj = obj + + # Delegate attribute lookup to internal obj + def __getattr__(self, name): + return getattr(self._obj, name) + + # Delegate attribute assignment + def __setattr__(self, name, value): + if name.startswith('_'): + super().__setattr__(name, value) # Call original __setattr__ + else: + setattr(self._obj, name, value) + +if __name__ == '__main__': + class A: + def __init__(self, x): + self.x = x + def spam(self): + print('A.spam') + + a = A(42) + p = Proxy(a) + print(p.x) + print(p.spam()) + p.x = 37 + print('Should be 37:', p.x) + print('Should be 37:', a.x) + + diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py new file mode 100644 index 0000000..cb47a16 --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example4.py @@ -0,0 +1,26 @@ +# Tricky initialization problem involving multiple inheritance. +# Does NOT use super() + +class Base: + def __init__(self): + print('Base.__init__') + +class A(Base): + def __init__(self): + Base.__init__(self) + print('A.__init__') + +class B(Base): + def __init__(self): + Base.__init__(self) + print('B.__init__') + +class C(A,B): + def __init__(self): + A.__init__(self) + B.__init__(self) + print('C.__init__') + +if __name__ == '__main__': + # Please observe double call of Base.__init__ + c = C() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py new file mode 100644 index 0000000..15503c2 --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_a_parent_class/example5.py @@ -0,0 +1,25 @@ +# Tricky initialization problem involving multiple inheritance. +# Uses super() + +class Base: + def __init__(self): + print('Base.__init__') + +class A(Base): + def __init__(self): + super().__init__() + print('A.__init__') + +class B(Base): + def __init__(self): + super().__init__() + print('B.__init__') + +class C(A,B): + def __init__(self): + super().__init__() # Only one call to super() here + print('C.__init__') + +if __name__ == '__main__': + # Observe that each class initialized only once + c = C() diff --git a/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py b/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py new file mode 100644 index 0000000..87ab944 --- /dev/null +++ b/chef/cookbooks/python/src/8/calling_a_method_on_an_object_given_the_name_as_a_string/example.py @@ -0,0 +1,40 @@ +# Example of calling methods by name + +import math +class Point: + def __init__(self, x, y): + self.x = x + self.y = y + + def __repr__(self): + return 'Point({!r:},{!r:})'.format(self.x, self.y) + + def distance(self, x, y): + return math.hypot(self.x - x, self.y - y) + +p = Point(2,3) + +# Method 1 : Use getattr +d = getattr(p, 'distance')(0, 0) # Calls p.distance(0, 0) +print(d) + +# Method 2: Use methodcaller +import operator +d = operator.methodcaller('distance', 0, 0)(p) +print(d) + +# Application in sorting +points = [ + Point(1, 2), + Point(3, 0), + Point(10, -3), + Point(-5, -7), + Point(-1, 8), + Point(3, 2) +] + +# Sort by distance from origin (0, 0) +points.sort(key=operator.methodcaller('distance', 0, 0)) +for p in points: + print(p) + diff --git a/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py b/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py new file mode 100644 index 0000000..c08a049 --- /dev/null +++ b/chef/cookbooks/python/src/8/changing_the_string_representation_of_instances/example.py @@ -0,0 +1,9 @@ +class Pair: + def __init__(self, x, y): + self.x = x + self.y = y + def __repr__(self): + return 'Pair({0.x!r}, {0.y!r})'.format(self) + def __str__(self): + return '({0.x}, {0.y})'.format(self) + diff --git a/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py b/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py new file mode 100644 index 0000000..b15a6e7 --- /dev/null +++ b/chef/cookbooks/python/src/8/creating_a_new_kind_of_class_or_instance_attribute/example1.py @@ -0,0 +1,34 @@ +# Descriptor attribute for an integer type-checked attribute +class Integer: + def __init__(self, name): + self.name = name + + def __get__(self, instance, cls): + if instance is None: + return self + else: + return instance.__dict__[self.name] + + def __set__(self, instance, value): + if not isinstance(value, int): + raise TypeError('Expected an int') + instance.__dict__[self.name] = value + + def __delete__(self, instance): + del instance.__dict__[self.name] + +class Point: + x = Integer('x') + y = Integer('y') + def __init__(self, x, y): + self.x = x + self.y = y + +if __name__ == '__main__': + p = Point(2, 3) + print(p.x) + p.y = 5 + try: + p.x = 2.3 + except TypeError as e: + print(e) diff --git a/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py b/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py new file mode 100644 index 0000000..b02204a --- /dev/null +++ b/chef/cookbooks/python/src/8/creating_an_instance_without_invoking_init/example.py @@ -0,0 +1,35 @@ +from time import localtime + +class Date: + def __init__(self, year, month, day): + self.year = year + self.month = month + self.day = day + + # Class method that bypasses __init__ + @classmethod + def today(cls): + d = cls.__new__(cls) + t = localtime() + d.year = t.tm_year + d.month = t.tm_mon + d.day = t.tm_mday + return d + +d = Date.__new__(Date) +print(d) +print(hasattr(d,'year')) + +data = { + 'year' : 2012, + 'month' : 8, + 'day' : 29 +} + +d.__dict__.update(data) +print(d.year) +print(d.month) + +d = Date.today() +print(d.year, d.month, d.day) + diff --git a/chef/cookbooks/python/test/cookbooks/python_test/README.md b/chef/cookbooks/python/test/cookbooks/python_test/README.md new file mode 100644 index 0000000..cc621f0 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/README.md @@ -0,0 +1,15 @@ +python_test Cookbook +==================== + +This cookbook tests the pip and virtualenv providers + +Requirements +------------ + +#### packages +- `python` - Version *2.5* or higher + +License and Authors +------------------- +Authors: Scott Likens + Sean Porter diff --git a/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb b/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb new file mode 100644 index 0000000..5642e62 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/files/default/tests/minitest/cook-3084_test.rb @@ -0,0 +1,17 @@ +require 'minitest/spec' + +describe_recipe 'python_test::cook-3084' do + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + it "created a virtualenv in cook-3084" do + result = assert_sh("cook-3084/bin/python -c 'import sys; from os.path import basename; print basename(sys.prefix)'") + assert_match /cook-3084\n/, result + end + + it "created a virtualenv in cook-3084-interpreter" do + result = assert_sh("cook-3084-interpreter/bin/python -c 'import sys; from os.path import basename; print basename(sys.prefix)'") + assert_match /cook-3084-interpreter\n/, result + end +end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb b/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb new file mode 100644 index 0000000..e5055d8 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/metadata.rb @@ -0,0 +1,7 @@ +name 'python_test' +maintainer 'Scott Likens' +maintainer_email 'scott@mopub.com' +license 'Apache 2.0' +description 'Installs/Configures python_test' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.1.0' diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb b/chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb new file mode 100644 index 0000000..3f01581 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/recipes/cook-3084.rb @@ -0,0 +1,35 @@ +# +# Author:: Alex Kiernan () +# Cookbook Name:: python +# Recipe:: cook-3084 +# +# Copyright 2013, Alex Kiernan +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "python" + +python_virtualenv "cook-3084" do +end + +python_virtualenv "cook-3084-interpreter" do + # on EL5 the default python we install is called python26 + if !node['python']['install_method'].eql?("source") && + platform_family?('rhel') && + node['platform_version'].split('.').first.to_i < 6 + interpreter '/usr/bin/python26' + else + interpreter 'python' + end +end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb new file mode 100644 index 0000000..b0ad314 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_exert.rb @@ -0,0 +1,35 @@ +# +# Author:: Scott M. Likens +# Cookbook Name:: python +# Recipe:: test_exert +# +# Copyright 2013, MoPub, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +python_virtualenv "#{Chef::Config[:file_cache_path]}/virtualenv" do + interpreter "python" + owner "root" + group "root" + action :create +end + +python_pip "boto" do + action :install + virtualenv "#{Chef::Config[:file_cache_path]}/virtualenv" +end + +python_pip "psutil" do + action :install +end diff --git a/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb new file mode 100644 index 0000000..66e1a64 --- /dev/null +++ b/chef/cookbooks/python/test/cookbooks/python_test/recipes/test_virtualenv.rb @@ -0,0 +1,35 @@ +# +# Author:: Sean Porter +# Cookbook Name:: python +# Recipe:: test_virtualenv +# +# Copyright 2013, Heavy Water Operations, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +python_virtualenv "/tmp/virtualenv" do + owner "root" + group "root" + action :create +end + +python_virtualenv "isolated python environment" do + path "/tmp/tobedestroyed" + action :create +end + +python_virtualenv "deleting the isolated python environment" do + path "/tmp/tobedestroyed" + action :delete +end diff --git a/chef/cookbooks/python/test/integration/exert/bats/exert.bats b/chef/cookbooks/python/test/integration/exert/bats/exert.bats new file mode 100644 index 0000000..714e3e1 --- /dev/null +++ b/chef/cookbooks/python/test/integration/exert/bats/exert.bats @@ -0,0 +1,13 @@ +#!/usr/bin/env bats + +@test "virtualenv test environment should exist" { + [ -f "/tmp/kitchen-chef-solo/cache/virtualenv/bin/activate" ] +} + +@test "virtualenv test environment should be owned by root" { + ls -l /tmp/kitchen-chef-solo/cache/virtualenv | grep "root root" +} + +@test "virtualenv test environment should have boto working" { + /tmp/kitchen-chef-solo/cache/virtualenv/bin/python -c 'import boto; boto.Version' +} diff --git a/chef/cookbooks/python/test/integration/source/bats/source.bats b/chef/cookbooks/python/test/integration/source/bats/source.bats new file mode 100644 index 0000000..ae1fe98 --- /dev/null +++ b/chef/cookbooks/python/test/integration/source/bats/source.bats @@ -0,0 +1,9 @@ +#!/usr/bin/env bats + +@test "python bin should exist" { + [ -x "/usr/local/bin/python" ] +} + +@test "python should be version 2.7.5" { + /usr/local/bin/python -c 'import sys; print sys.version' | grep '2.7.5' +} diff --git a/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats b/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats new file mode 100644 index 0000000..f5910af --- /dev/null +++ b/chef/cookbooks/python/test/integration/virtualenv/bats/virtualenv.bats @@ -0,0 +1,17 @@ +#!/usr/bin/env bats + +@test "virtualenv test environment should exist" { + [ -f "/tmp/virtualenv/bin/activate" ] +} + +@test "virtualenv test environment should be owned by root" { + ls -l /tmp/virtualenv | grep "root root" +} + +@test "virtualenv test environment should have a working python" { + /tmp/virtualenv/bin/python -c 'import sys; print sys.version' +} + +@test "virtualenv resource should be able to delete an environment" { + [ ! -d "/tmp/tobedestroyed" ] +} diff --git a/chef/cookbooks/rabbitmq/.kitchen.yml b/chef/cookbooks/rabbitmq/.kitchen.yml new file mode 100644 index 0000000..464b6c8 --- /dev/null +++ b/chef/cookbooks/rabbitmq/.kitchen.yml @@ -0,0 +1,192 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-13.04 + driver_config: + box: opscode-ubuntu-13.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + run_list: + - recipe[yum::epel] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + run_list: + - recipe[yum::epel] + +- name: fedora-18 + driver_config: + box: opscode-fedora-18 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode-fedora-18_provisionerless.box + run_list: {} + +# add once the bento image is fixed +# - name: debian-7.1 +# driver_config: +# box: opscode-debian-7.1 +# box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box +# run_list: +# - recipe[apt] + +# image not downloading +# - name: smartos-13.1 +# driver_config: +# box: smartos-base1310 +# box_url: http://dlc-int.openindiana.org/aszeszo/vagrant/smartos-base1310-64-virtualbox-20130806.box +# run_list: +# - recipe[rabbitmq] + +# http://tickets.opscode.com/browse/COOK-2467 +# - name: omnios-r151002 +# driver_config: +# box: omnios-r151002 +# box_url: http://omnios.omniti.com/media/omnios-latest.box +# run_list: +# - recipe[rabbitmq] + +suites: +- name: default + excludes: + - omnios-r151002 + - smartos-13.1 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::default] + attributes: {} + +- name: default-esl + excludes: + - centos-5.9 + - fedora-18 + - omnios-r151002 + - smartos-13.1 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::default] + attributes: + erlang: + install_method: 'esl' + +- name: upstart + excludes: + - centos-5.9 + - centos-6.4 + - fedora-18 + - ubuntu-13.04 + - omnios-r151002 + - smartos-13.1 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::default] + attributes: + rabbitmq: + job_control: 'upstart' + +- name: distro-version + excludes: + - centos-5.9 + - fedora-18 + - ubuntu-10.04 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::default] + attributes: + rabbitmq: + use_distro_version: true + +- name: mgmt_console + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::mgmt_console] + attributes: {} + +- name: cook-2151-3489 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::cook-2151-3489] + attributes: + rabbitmq: + disk_free_limit_relative: 1.0 + vm_memory_high_watermark: 0.5 + max_file_descriptors: 2048 + open_file_limit: 102400 + +- name: cook-2705 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::default] + attributes: + rabbitmq: + tcp_listen_keepalive: true + +# stress test for lwrps +- name: lwrps-default + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::lwrps] + attributes: + rabbitmq: + enabled_plugins: ["rabbitmq_stomp", "rabbitmq_shovel", "rabbitmq_stomp"] + disabled_plugins: ["nonexistant_plugin", "rabbitmq_shovel"] + enabled_users: [ + {name: "kitchen1", password: "test", tag: "tag1", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen2", password: "test", tag: "tag2", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}, {vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen3", password: "test", tag: "tag3", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen1", password: "test", tag: "tag4", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]} + ] + disabled_users: ["nonexistant_user", "kitchen2"] + disabled_policies: ["nonexistant_policy"] + virtualhosts: ["kitchen", "kitchen"] + disabled_virtualhosts: ["nonexistant_vhost"] + +# stress test for lwrps with distro packages +- name: lwrps-distro + excludes: + - centos-5.9 + - centos-6.4 + - fedora-18 + - ubuntu-10.04 + - ubuntu-12.04 + run_list: + - recipe[minitest-handler] + - recipe[rabbitmq_test::lwrps] + attributes: + rabbitmq: + use_distro_version: true + enabled_plugins: ["rabbitmq_stomp", "rabbitmq_shovel", "rabbitmq_stomp"] + disabled_plugins: ["nonexistant_plugin", "rabbitmq_shovel"] + enabled_users: [ + {name: "kitchen1", password: "test", tag: "tag1", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen2", password: "test", tag: "tag2", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}, {vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen3", password: "test", tag: "tag3", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]}, + {name: "kitchen1", password: "test", tag: "tag4", rights: [{vhost: "kitchen", conf: ".*", write: ".*", read: ".*"}]} + ] + disabled_users: ["nonexistant_user", "kitchen2"] + disabled_policies: ["nonexistant_policy"] + virtualhosts: ["kitchen", "kitchen"] + disabled_virtualhosts: ["nonexistant_vhost"] diff --git a/chef/cookbooks/rabbitmq/Berksfile b/chef/cookbooks/rabbitmq/Berksfile new file mode 100644 index 0000000..f3ef5a6 --- /dev/null +++ b/chef/cookbooks/rabbitmq/Berksfile @@ -0,0 +1,10 @@ +site :opscode + +metadata + +group :integration do + cookbook "minitest-handler" + cookbook "apt" + cookbook "yum" + cookbook "rabbitmq_test", :path => "./test/cookbooks/rabbitmq_test" +end diff --git a/chef/cookbooks/rabbitmq/CHANGELOG.md b/chef/cookbooks/rabbitmq/CHANGELOG.md new file mode 100644 index 0000000..128c5fa --- /dev/null +++ b/chef/cookbooks/rabbitmq/CHANGELOG.md @@ -0,0 +1,113 @@ +rabbitmq Cookbook CHANGELOG +=========================== +This file is used to list changes made in each version of the rabbitmq cookbook. + + +v2.3.0 +------ +### Improvement +- **[COOK-3369](https://tickets.opscode.com/browse/COOK-3369)** - Add SUSE support +- **[COOK-3320](https://tickets.opscode.com/browse/COOK-3320)** - Configure bind and cluster over a specified addr +- **[COOK-3138](https://tickets.opscode.com/browse/COOK-3138)** - Do not log RabbitMQ password +- **[COOK-2803](https://tickets.opscode.com/browse/COOK-2803)** - Bind erlang networking to localhost (attribute-driven) + +v2.2.0 +------ +### Improvement +- Greatly expanded Test Kitchen coverage and platform support +- added support for disabling policies and virtualhosts through attributes +- added support for using with the erlang::esl recipe +- [COOK-2705]: Add ability to change tcp_listen_options in config +- [COOK-2397]: Added upstart support to rabbitmq cookbook +- [COOK-2830]: Use a notify for server restart, instead of defining a new service +- [COOK-3384]: Added ability to change user password +- [COOK-3489]: Add attribute to set open file limit + +### Bug +- [COOK-3011]: Incorrect apt source test causes Chef run to fail on Ubuntu +- [COOK-3438]: RabbitMQ fixes for Fedora 19 + +v2.1.2 +------ +### Improvement +- [COOK-3099]: policy resource should support optional vhost argument + +### Bug + +- [COOK-3078]: rabbitmq password is not quoted or escaped on add_user +- [COOK-3079]: rabbitmq permissions check doesn't match, resulting in non-idempotency + +v2.1.0 +------ +### Bug +- [COOK-2828]: Rabbitmq Clustering doesn't work properly +- [COOK-2975]: rabbitmq has foodcritic failures + +### New Feature +- [COOK-2575]: LWRP for setting policies + +v2.0.0 +------ +- Major v2.0 changes are documented in the README. +- [COOK-2391] - Added support for verify verify_peer and fail_if_no_peer_cert true +- [COOK-2153] - Fix of user LWRP +- [COOK-2180] - Plugin management via node attributes +- [COOK-2201] - Use the proper syntax when using rabbitmq 3.0 instead of 2.x +- [COOK-2210] - User management via node attributes +- [COOK-2211] - Virtualhost management via node attributes +- [COOK-2235] - RabbitMQ bin path isn't necessarily part of PATH for the plugin provider +- [COOK-2392] - correctly configure a rabbitmq cluster +- [COOK-2366] - Default recipe doesn't create mnesia dir +- [COOK-2416] - Add support for clearing tags. + +v1.8.0 +------ +- [COOK-2151] - Add config options for `disk_free_limit` and `vm_memory_high_watermark` via attributes + +v1.7.0 +------ +- [COOK-1850] - oracle linux support +- [COOK-1873] - add `set_user_tag` action to `rabbitmq_user` LWRP +- [COOK-1878] - :immediately action causes clustering to fail +- [COOK-1888] - smartos support + +v1.6.4 +------ +- [COOK-1684] - Unify behavior of debian and rhel clones in the rabbitmq cookbook +- [COOK-1724] - enable using the distro release of rabbitmq instead of the RabbitMQ.org version + +v1.6.2 +------ +- [COOK-1552] - removed rogue single quote from rabbitmq ssl configuration + +v1.6.0 +------ +- [COOK-1496] - explicitly include the apt recipe +- [COOK-1501] - Allow user to enable yum-based installation of rabbitmq via an attribute +- [COOK-1503] - Recipe to enable rabbitmq web management console + +v1.5.0 +------ +This version requires apt cookbook v1.4.4 (reflected in metadata). + +- [COOK-1216] - add amazon linux to RHELish platforms +- [COOK-1217] - specify version, for RHELish platforms +- [COOK-1219] - immediately restart service on config update +- [COOK-1317] - fix installation of old version from ubuntu APT repo +- [COOK-1331] - LWRP for enabling/disabling rabbitmq plugins +- [COOK-1386] - increment rabbitmq version to 2.8.4 +- [COOK-1432] - resolve foodcritic warnings +- [COOK-1438] - add fedora to RHELish platforms + +v1.4.1 +------ +- [COOK-1386] - Bumped version to 2.8.4 +- rabbitmq::default now includes erlang::default + +v1.4.0 +------ +- [COOK-911] - Auto clustering support + +v1.3.2 +------ +- [COOK-585] - manage rabbitmq-server service diff --git a/chef/cookbooks/rabbitmq/CONTRIBUTING b/chef/cookbooks/rabbitmq/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/rabbitmq/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/rabbitmq/LICENSE b/chef/cookbooks/rabbitmq/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/rabbitmq/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/rabbitmq/README.md b/chef/cookbooks/rabbitmq/README.md new file mode 100644 index 0000000..f2571f5 --- /dev/null +++ b/chef/cookbooks/rabbitmq/README.md @@ -0,0 +1,175 @@ +rabbitmq Cookbook +================= +This is a cookbook for managing RabbitMQ with Chef. It is intended for 2.6.1 or later releases. + +**Version 2.0 Changes** + +The 2.0 release of the cookbook defaults to using the latest version available from RabbitMQ.com via direct download of the package. This was done to simplify the installation options to either distro package or direct download. The attributes `use_apt` and `use_yum` have been removed as have the `apt` and `yum` cookbook dependencies. The user LWRP action `:set_user_tags` was changed to `:set_tags` for consistency with other actions. + + +Requirements +------------ +This cookbook depends on the `erlang` cookbook. + +Please refer to the [TESTING file](TESTING.md) to see the currently (and passing) tested platforms. The release was tested with (rabbitmq.com/distro version): +- CentOS 5.9: 3.1.5 (distro release unsupported) +- CentOS 6.4: 3.1.5/2.6.1 (no lwrps support) +- Fedora 18: 3.1.5 (distro release unsupported) +- Ubuntu 10.04: 3.1.5 (distro release unsupported) +- Ubuntu 12.04: 3.1.5/2.7.1 (no lwrps support) +- Ubuntu 13.04: 3.1.5/3.0.2 + + +Recipes +------- +### default +Installs `rabbitmq-server` from RabbitMQ.com via direct download of the installation package or using the distribution version. Depending on your distribution, the provided version may be quite old so they are disabled by default. If you want to use the distro version, set the attribute `['rabbitmq']['use_distro_version']` to `true`. You may override the download URL attribute `['rabbitmq']['package']` if you wish to use a local mirror. + +The cluster recipe is now combined with the default and will now auto-cluster. Set the `['rabbitmq']['cluster']` attribute to `true`, `['rabbitmq']['cluster_disk_nodes']` array of `node@host` strings that describe which you want to be disk nodes and then set an alphanumeric string for the `erlang_cookie`. + +To enable SSL turn `ssl` to `true` and set the paths to your cacert, cert and key files. + +### mgmt_console +Installs the `rabbitmq_management` and `rabbitmq_management_visualiser` plugins. + +### plugin_management +Enables any plugins listed in the `node['rabbitmq']['enabled_plugins']` and disables any listed in `node['rabbitmq'][disabled_plugins']` attributes. + +### policy_management +Enables any policies listed in the `node['rabbitmq'][policies]` and disables any listed in `node['rabbitmq'][disabled_policies]` attributes. + +### user_management +Enables any users listed in the `node['rabbitmq']['enabled_users]` and disables any listed in `node['rabbitmq'][disabled_users]` attributes. + +### virtualhost_management +Enables any vhosts listed in the `node['rabbitmq'][virtualhosts]` and disables any listed in `node['rabbitmq'][disabled_virtualhosts]` attributes. + + +Resources/Providers +------------------- +There are 4 LWRPs for interacting with RabbitMQ. + +### plugin +Enables or disables a rabbitmq plugin. Plugins are not supported for releases prior to 2.7.0. + +- `:enable` enables a `plugin` +- `:disable` disables a `plugin` + +#### Examples +```ruby +rabbitmq_plugin "rabbitmq_stomp" do + action :enable +end +``` + +```ruby +rabbitmq_plugin "rabbitmq_shovel" do + action :disable +end +``` + +### policy +sets or clears a rabbitmq policy. + +- `:set` sets a `policy` +- `:clear` clears a `policy` +- `:list` lists `policy`s + +#### Examples +```ruby +rabbitmq_policy "ha-all" do + pattern "^(?!amq\\.).*" + params {"ha-mode"=>"all"} + priority 1 + action :set +end +``` + +```ruby +rabbitmq_policy "ha-all" do + action :clear +end +``` + +### user +Adds and deletes users, fairly simplistic permissions management. + +- `:add` adds a `user` with a `password` +- `:delete` deletes a `user` +- `:set_permissions` sets the `permissions` for a `user`, `vhost` is optional +- `:clear_permissions` clears the permissions for a `user` +- `:set_tags` set the tags on a user +- `:clear_tags` clear any tags on a user +- `:change_password` set the `password` for a `user` + +#### Examples +```ruby +rabbitmq_user "guest" do + action :delete +end +``` + +```ruby +rabbitmq_user "nova" do + password "sekret" + action :add +end +``` + +```ruby +rabbitmq_user "nova" do + vhost "/nova" + permissions ".* .* .*" + action :set_permissions +end +``` + +```ruby +rabbitmq_user "joe" do + tag "admin,lead" + action :set_tags +end +``` + +### vhost +Adds and deletes vhosts. + +- `:add` adds a `vhost` +- `:delete` deletes a `vhost` + +#### Examples +``` ruby +rabbitmq_vhost "/nova" do + action :add +end +``` + + +Limitations +----------- +For an already running cluster, these actions still require manual intervention: +- changing the :erlang_cookie +- turning :cluster from true to false + + +License & Authors +----------------- +- Author:: Benjamin Black +- Author:: Daniel DeLeo +- Author:: Matt Ray () + +```text +Copyright (c) 2009-2013, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/rabbitmq/TESTING.md b/chef/cookbooks/rabbitmq/TESTING.md new file mode 100644 index 0000000..8380623 --- /dev/null +++ b/chef/cookbooks/rabbitmq/TESTING.md @@ -0,0 +1,39 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test + +This cookbook has the following Test-Kitchen coverage: + +| Test Coverage | Ubuntu 10.04 | Ubuntu 12.04 | Ubuntu 13.04 | Centos 5.9 | Centos 6.4 | Fedora 18 | Debian 7.1 | SmartOS 13.1 | OmniOS r151002 | +| -------------- |:-------------:|:------------:|:------------:|:----------:|:----------:|:---------:|:----------:|:------------:|:--------------:| +| default | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | +| default-esl | **Y** | **Y** | **Y** | **N** | **Y** | **N** | **N** | **N** | **N** | +| upstart | **Y** | **Y** | **Y** | **N** | **N** | **N** | **N** | **N** | **N** | +| distro-version | **N** | **Y** | **Y** | **N** | **Y** | **N** | **N** | **N** | **N** | +| mgmt_console | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | +| cook-2151-3489 | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | +| cook-2705 | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | +| lwrps-default | **Y** | **Y** | **Y** | **Y** | **Y** | **Y** | **N** | **N** | **N** | +| lwrps-distro | **N** | **N** | **Y** | **N** | **N** | **N** | **N** | **N** | **N** | diff --git a/chef/cookbooks/rabbitmq/attributes/default.rb b/chef/cookbooks/rabbitmq/attributes/default.rb new file mode 100644 index 0000000..aac6eb3 --- /dev/null +++ b/chef/cookbooks/rabbitmq/attributes/default.rb @@ -0,0 +1,100 @@ +# Latest RabbitMQ.com version to install +default['rabbitmq']['version'] = '3.1.5' +# The distro versions may be more stable and have back-ported patches +default['rabbitmq']['use_distro_version'] = false + +# being nil, the rabbitmq defaults will be used +default['rabbitmq']['nodename'] = nil +default['rabbitmq']['address'] = nil +default['rabbitmq']['port'] = nil +default['rabbitmq']['config'] = nil +default['rabbitmq']['logdir'] = nil +default['rabbitmq']['mnesiadir'] = "/var/lib/rabbitmq/mnesia" +default['rabbitmq']['service_name'] = 'rabbitmq-server' + +# config file location +# http://www.rabbitmq.com/configure.html#define-environment-variables +# "The .config extension is automatically appended by the Erlang runtime." +default['rabbitmq']['config_root'] = "/etc/rabbitmq" +default['rabbitmq']['config'] = "/etc/rabbitmq/rabbitmq" +default['rabbitmq']['erlang_cookie_path'] = '/var/lib/rabbitmq/.erlang.cookie' + +# rabbitmq.config defaults +default['rabbitmq']['default_user'] = 'guest' +default['rabbitmq']['default_pass'] = 'guest' + +# bind erlang networking to localhost +default['rabbitmq']['local_erl_networking'] = false + +# bind rabbit and erlang networking to an address +default['rabbitmq']['erl_networking_bind_address'] = nil + +#clustering +default['rabbitmq']['cluster'] = false +default['rabbitmq']['cluster_disk_nodes'] = [] +default['rabbitmq']['erlang_cookie'] = 'AnyAlphaNumericStringWillDo' + +# resource usage +default['rabbitmq']['disk_free_limit_relative'] = nil +default['rabbitmq']['vm_memory_high_watermark'] = nil +default['rabbitmq']['max_file_descriptors'] = 1024 +default['rabbitmq']['open_file_limit'] = nil + +# job control +default['rabbitmq']['job_control'] = 'initd' + +#ssl +default['rabbitmq']['ssl'] = false +default['rabbitmq']['ssl_port'] = 5671 +default['rabbitmq']['ssl_cacert'] = '/path/to/cacert.pem' +default['rabbitmq']['ssl_cert'] = '/path/to/cert.pem' +default['rabbitmq']['ssl_key'] = '/path/to/key.pem' +default['rabbitmq']['ssl_verify'] = 'verify_none' +default['rabbitmq']['ssl_fail_if_no_peer_cert'] = false + +#tcp listen options +default['rabbitmq']['tcp_listen_packet'] = 'raw' +default['rabbitmq']['tcp_listen_reuseaddr'] = true +default['rabbitmq']['tcp_listen_backlog'] = 128 +default['rabbitmq']['tcp_listen_nodelay'] = true +default['rabbitmq']['tcp_listen_exit_on_close'] = false +default['rabbitmq']['tcp_listen_keepalive'] = false + +#virtualhosts +default['rabbitmq']['virtualhosts'] = [] +default['rabbitmq']['disabled_virtualhosts'] = [] + +#users +default['rabbitmq']['enabled_users'] = + [{ :name => "guest", :password => "guest", :rights => + [{:vhost => nil , :conf => ".*", :write => ".*", :read => ".*"}] + }] +default['rabbitmq']['disabled_users'] =[] + +#plugins +default['rabbitmq']['enabled_plugins'] = [] +default['rabbitmq']['disabled_plugins'] = [] + +#platform specific settings +case node['platform_family'] +when 'debian' + default['rabbitmq']['package'] = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" +when 'rhel','fedora' + default['rabbitmq']['package'] = "https://www.rabbitmq.com/releases/rabbitmq-server/v#{node['rabbitmq']['version']}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" +when 'smartos' + default['rabbitmq']['service_name'] = 'rabbitmq' + default['rabbitmq']['config_root'] = '/opt/local/etc/rabbitmq' + default['rabbitmq']['config'] = '/opt/local/etc/rabbitmq/rabbitmq' + default['rabbitmq']['erlang_cookie_path'] = '/var/db/rabbitmq/.erlang.cookie' +end + +# Example HA policies +default['rabbitmq']['policies']['ha-all']['pattern'] = "^(?!amq\\.).*" +default['rabbitmq']['policies']['ha-all']['params'] = { "ha-mode" => "all" } +default['rabbitmq']['policies']['ha-all']['priority'] = 0 + +default['rabbitmq']['policies']['ha-two']['pattern'] = "^two\." +default['rabbitmq']['policies']['ha-two']['params'] = { "ha-mode" => "exactly", "ha-params" => 2 } +default['rabbitmq']['policies']['ha-two']['priority'] = 1 + +default['rabbitmq']['disabled_policies'] = [] diff --git a/chef/cookbooks/rabbitmq/metadata.rb b/chef/cookbooks/rabbitmq/metadata.rb new file mode 100644 index 0000000..4def5bf --- /dev/null +++ b/chef/cookbooks/rabbitmq/metadata.rb @@ -0,0 +1,104 @@ +name "rabbitmq" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures RabbitMQ server" +version "2.3.0" +recipe "rabbitmq", "Install and configure RabbitMQ" +recipe "rabbitmq::cluster", "Set up RabbitMQ clustering." +recipe "rabbitmq::plugin_management", "Manage plugins with node attributes" +recipe "rabbitmq::virtualhost_management", "Manage virtualhost with node attributes" +recipe "rabbitmq::user_management", "Manage users with node attributes" +depends "erlang", ">= 0.9" + +%w{ubuntu debian linuxmint redhat centos scientific amazon fedora oracle smartos suse}.each do |os| + supports os +end + +attribute "rabbitmq", + :display_name => "RabbitMQ", + :description => "Hash of RabbitMQ attributes", + :type => "hash" + +attribute "rabbitmq/nodename", + :display_name => "RabbitMQ Erlang node name", + :description => "The Erlang node name for this server.", + :default => "node['hostname']" + +attribute "rabbitmq/address", + :display_name => "RabbitMQ server IP address", + :description => "IP address to bind." + +attribute "rabbitmq/port", + :display_name => "RabbitMQ server port", + :description => "TCP port to bind." + +attribute "rabbitmq/config", + :display_name => "RabbitMQ config file to load", + :description => "Path to the rabbitmq.config file, if any." + +attribute "rabbitmq/logdir", + :display_name => "RabbitMQ log directory", + :description => "Path to the directory for log files." + +attribute "rabbitmq/mnesiadir", + :display_name => "RabbitMQ Mnesia database directory", + :description => "Path to the directory for Mnesia database files." + +attribute "rabbitmq/cluster", + :display_name => "RabbitMQ clustering", + :description => "Whether to activate clustering.", + :default => "no" + +attribute "rabbitmq/cluster_config", + :display_name => "RabbitMQ clustering configuration file", + :description => "Path to the clustering configuration file, if cluster is yes.", + :default => "/etc/rabbitmq/rabbitmq_cluster.config" + +attribute "rabbitmq/cluster_disk_nodes", + :display_name => "RabbitMQ cluster disk nodes", + :description => "Array of member Erlang nodenames for the disk-based storage nodes in the cluster.", + :default => [], + :type => "array" + +attribute "rabbitmq/erlang_cookie", + :display_name => "RabbitMQ Erlang cookie", + :description => "Access cookie for clustering nodes. There is no default." + +attribute "rabbitmq/virtualhosts", + :display_name => "Virtualhosts on rabbitmq instance", + :description => "List all virtualhosts that will exist", + :default => [], + :type => "array" + +attribute "rabbitmq/enabled_users", + :display_name => "Users and their rights on rabbitmq instance", + :description => "Users and description of their rights", + :default => [{ :name => "guest", :password => "guest", :rights => [{:vhost => nil , :conf => ".*", :write => ".*", :read => ".*"}]}], + :type => "array" + +attribute "rabbitmq/disabled_users", + :display_name => "Disabled users", + :description => "List all users that will be deactivated", + :default => [], + :type => "array" + +attribute "rabbitmq/enabled_plugins", + :display_name => "Enabled plugins", + :description => "List all plugins that will be activated", + :default => [], + :type => "array" + +attribute "rabbitmq/disabled_plugins", + :display_name => "Disabled plugins", + :description => "List all plugins that will be deactivated", + :default => [], + :type => "array" + +attribute "rabbitmq/local_erl_networking", + :display_name => "Local Erlang networking", + :description => "Bind erlang networking to localhost" + +attribute "rabbitmq/erl_networking_bind_address", + :display_name => "Erl Networking Bind Address", + :description => "Bind Rabbit and erlang networking to an address" diff --git a/chef/cookbooks/rabbitmq/providers/plugin.rb b/chef/cookbooks/rabbitmq/providers/plugin.rb new file mode 100644 index 0000000..fbb8990 --- /dev/null +++ b/chef/cookbooks/rabbitmq/providers/plugin.rb @@ -0,0 +1,55 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: plugin +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def plugins_bin_path(return_array=false) + path = ENV.fetch('PATH') + ':/usr/lib/rabbitmq/bin' + return_array ? path.split(':') : path +end + +def plugin_enabled?(name) + cmdStr = "rabbitmq-plugins list -e '#{name}\\b'" + cmd = Mixlib::ShellOut.new(cmdStr) + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.environment['PATH'] = plugins_bin_path + cmd.run_command + Chef::Log.debug "rabbitmq_plugin_enabled?: #{cmdStr}" + Chef::Log.debug "rabbitmq_plugin_enabled?: #{cmd.stdout}" + cmd.error! + cmd.stdout =~ /\b#{name}\b/ +end + +action :enable do + unless plugin_enabled?(new_resource.plugin) + execute "rabbitmq-plugins enable #{new_resource.plugin}" do + Chef::Log.info "Enabling RabbitMQ plugin '#{new_resource.plugin}'." + path plugins_bin_path(true) + new_resource.updated_by_last_action(true) + end + end +end + +action :disable do + if plugin_enabled?(new_resource.plugin) + execute "rabbitmq-plugins disable #{new_resource.plugin}" do + Chef::Log.info "Disabling RabbitMQ plugin '#{new_resource.plugin}'." + path plugins_bin_path(true) + new_resource.updated_by_last_action(true) + end + end +end diff --git a/chef/cookbooks/rabbitmq/providers/policy.rb b/chef/cookbooks/rabbitmq/providers/policy.rb new file mode 100644 index 0000000..1d957da --- /dev/null +++ b/chef/cookbooks/rabbitmq/providers/policy.rb @@ -0,0 +1,86 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: policy +# +# Author: Robert Choi +# Copyright 2013 by Robert Choi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def policy_exists?(name) + cmd = Mixlib::ShellOut.new("rabbitmqctl list_policies |grep '#{name}\\b'") + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.run_command + begin + cmd.error! + true + rescue + false + end +end + +action :set do + unless policy_exists?(new_resource.policy) + cmd = "rabbitmqctl set_policy" + cmd << " -p #{new_resource.vhost}" unless new_resource.vhost.nil? + cmd << " #{new_resource.policy}" + cmd << " \"#{new_resource.pattern}\"" + cmd << " '{" + + first_param = true + new_resource.params.each do |key, value| + unless first_param + cmd << "," + end + if value.kind_of? String + cmd << "\"#{key}\":\"#{value}\"" + else + cmd << "\"#{key}\":#{value}" + end + first_param = false + end + + cmd << "}'" + + if new_resource.priority + cmd << " #{new_resource.priority}" + end + + execute "set_policy #{new_resource.policy}" do + command cmd + end + + new_resource.updated_by_last_action(true) + Chef::Log.info "Done setting RabbitMQ policy '#{new_resource.policy}'." + end +end + +action :clear do + if policy_exists?(new_resource.policy) + execute "clear_policy #{new_resource.policy}" do + command "rabbitmqctl clear_policy #{new_resource.policy}" + end + + new_resource.updated_by_last_action(true) + Chef::Log.info "Done clearing RabbitMQ policy '#{new_resource.policy}'." + end +end + +action :list do + execute "list_policies" do + command "rabbitmqctl list_policies" + end + + new_resource.updated_by_last_action(true) +end diff --git a/chef/cookbooks/rabbitmq/providers/user.rb b/chef/cookbooks/rabbitmq/providers/user.rb new file mode 100644 index 0000000..6e050a2 --- /dev/null +++ b/chef/cookbooks/rabbitmq/providers/user.rb @@ -0,0 +1,173 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: user +# +# Copyright 2011-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def user_exists?(name) + cmdStr = "rabbitmqctl -q list_users |grep '^#{name}\\b'" + cmd = Mixlib::ShellOut.new(cmdStr) + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.run_command + Chef::Log.debug "rabbitmq_user_exists?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_exists?: #{cmd.stdout}" + begin + cmd.error! + true + rescue + false + end +end + +def user_has_tag?(name, tag) + tag = '"\[\]"' if tag.nil? + cmdStr = "rabbitmqctl -q list_users | grep \"^#{name}\\b\" | grep #{tag}" + cmd = Mixlib::ShellOut.new(cmdStr) + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.run_command + Chef::Log.debug "rabbitmq_user_has_tag?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_has_tag?: #{cmd.stdout}" + begin + cmd.error! + true + rescue Exception => e + false + end +end + +# does the user have the rights listed on the vhost? +# empty perm_list means we're checking for any permissions +def user_has_permissions?(name, vhost, perm_list = nil) + vhost = '/' if vhost.nil? + cmdStr = "rabbitmqctl -q list_user_permissions #{name} | grep \"^#{vhost}\\b\"" + cmd = Mixlib::ShellOut.new(cmdStr) + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.run_command + Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmdStr}" + Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmd.stdout}" + Chef::Log.debug "rabbitmq_user_has_permissions?: #{cmd.exitstatus}" + if perm_list.nil? && cmd.stdout.empty? #looking for empty and found nothing + Chef::Log.debug "rabbitmq_user_has_permissions?: no permissions found" + return false + end + if perm_list == cmd.stdout.split.drop(1) #existing match search + Chef::Log.debug "rabbitmq_user_has_permissions?: matching permissions already found" + return true + end + Chef::Log.debug "rabbitmq_user_has_permissions?: permissions found but do not match" + return false +end + +action :add do + unless user_exists?(new_resource.user) + if new_resource.password.nil? || new_resource.password.empty? + Chef::Application.fatal!("rabbitmq_user with action :add requires a non-nil/empty password.") + end + # To escape single quotes in a shell, you have to close the surrounding single quotes, add + # in an escaped single quote, and then re-open the original single quotes. + # Since this string is interpolated once by ruby, and then a second time by the shell, we need + # to escape the escape character ('\') twice. This is why the following is such a mess + # of leaning toothpicks: + new_password = new_resource.password.gsub("'", "'\\\\''") + cmdStr = "rabbitmqctl add_user #{new_resource.user} '#{new_password}'" + execute "rabbitmqctl add_user #{new_resource.user}" do + command cmdStr + Chef::Log.info "Adding RabbitMQ user '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end + end +end + +action :delete do + if user_exists?(new_resource.user) + cmdStr = "rabbitmqctl delete_user #{new_resource.user}" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_delete: #{cmdStr}" + Chef::Log.info "Deleting RabbitMQ user '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end + end +end + +action :set_permissions do + if !user_exists?(new_resource.user) + Chef::Application.fatal!("rabbitmq_user action :set_permissions fails with non-existant '#{new_resource.user}' user.") + end + perm_list = new_resource.permissions.split + unless user_has_permissions?(new_resource.user, new_resource.vhost, perm_list) + vhostOpt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? + cmdStr = "rabbitmqctl set_permissions #{vhostOpt} #{new_resource.user} \"#{perm_list.join("\" \"")}\"" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_set_permissions: #{cmdStr}" + Chef::Log.info "Setting RabbitMQ user permissions for '#{new_resource.user}' on vhost #{new_resource.vhost}." + new_resource.updated_by_last_action(true) + end + end +end + +action :clear_permissions do + if !user_exists?(new_resource.user) + Chef::Application.fatal!("rabbitmq_user action :clear_permissions fails with non-existant '#{new_resource.user}' user.") + end + if user_has_permissions?(new_resource.user, new_resource.vhost) + vhostOpt = "-p #{new_resource.vhost}" unless new_resource.vhost.nil? + cmdStr = "rabbitmqctl clear_permissions #{vhostOpt} #{new_resource.user}" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_clear_permissions: #{cmdStr}" + Chef::Log.info "Clearing RabbitMQ user permissions for '#{new_resource.user}' from vhost #{new_resource.vhost}." + new_resource.updated_by_last_action(true) + end + end +end + +action :set_tags do + if !user_exists?(new_resource.user) + Chef::Application.fatal!("rabbitmq_user action :set_tags fails with non-existant '#{new_resource.user}' user.") + end + unless user_has_tag?(new_resource.user, new_resource.tag) + cmdStr = "rabbitmqctl set_user_tags #{new_resource.user} #{new_resource.tag}" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_set_tags: #{cmdStr}" + Chef::Log.info "Setting RabbitMQ user '#{new_resource.user}' tags '#{new_resource.tag}'" + new_resource.updated_by_last_action(true) + end + end +end + +action :clear_tags do + if !user_exists?(new_resource.user) + Chef::Application.fatal!("rabbitmq_user action :clear_tags fails with non-existant '#{new_resource.user}' user.") + end + unless user_has_tag?(new_resource.user, '"\[\]"') + cmdStr = "rabbitmqctl set_user_tags #{new_resource.user}" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_clear_tags: #{cmdStr}" + Chef::Log.info "Clearing RabbitMQ user '#{new_resource.user}' tags." + new_resource.updated_by_last_action(true) + end + end +end + +action :change_password do + if user_exists?(new_resource.user) + cmdStr = "rabbitmqctl change_password #{new_resource.user} #{new_resource.password}" + execute cmdStr do + Chef::Log.debug "rabbitmq_user_change_password: #{cmdStr}" + Chef::Log.info "Editing RabbitMQ user '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end + end +end diff --git a/chef/cookbooks/rabbitmq/providers/vhost.rb b/chef/cookbooks/rabbitmq/providers/vhost.rb new file mode 100644 index 0000000..3b819d8 --- /dev/null +++ b/chef/cookbooks/rabbitmq/providers/vhost.rb @@ -0,0 +1,55 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: vhost +# +# Copyright 2011-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def vhost_exists?(name) + cmdStr = "rabbitmqctl -q list_vhosts | grep ^#{name}$" + cmd = Mixlib::ShellOut.new(cmdStr) + cmd.environment['HOME'] = ENV.fetch('HOME', '/root') + cmd.run_command + Chef::Log.debug "rabbitmq_vhost_exists?: #{cmdStr}" + Chef::Log.debug "rabbitmq_vhost_exists?: #{cmd.stdout}" + begin + cmd.error! + true + rescue + false + end +end + +action :add do + unless vhost_exists?(new_resource.vhost) + cmdStr = "rabbitmqctl add_vhost #{new_resource.vhost}" + execute cmdStr do + Chef::Log.debug "rabbitmq_vhost_add: #{cmdStr}" + Chef::Log.info "Adding RabbitMQ vhost '#{new_resource.vhost}'." + new_resource.updated_by_last_action(true) + end + end +end + +action :delete do + if vhost_exists?(new_resource.vhost) + cmdStr = "rabbitmqctl delete_vhost #{new_resource.vhost}" + execute cmdStr do + Chef::Log.debug "rabbitmq_vhost_delete: #{cmdStr}" + Chef::Log.info "Deleting RabbitMQ vhost '#{new_resource.vhost}'." + new_resource.updated_by_last_action(true) + end + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/default.rb b/chef/cookbooks/rabbitmq/recipes/default.rb new file mode 100644 index 0000000..85f4f83 --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/default.rb @@ -0,0 +1,186 @@ +# +# Cookbook Name:: rabbitmq +# Recipe:: default +# +# Copyright 2009, Benjamin Black +# Copyright 2009-2013, Opscode, Inc. +# Copyright 2012, Kevin Nuckolls +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe 'erlang' + +## Install the package +case node['platform_family'] +when 'debian' + # installs the required setsid command -- should be there by default but just in case + package 'util-linux' + + if node['rabbitmq']['use_distro_version'] + package 'rabbitmq-server' + else + remote_file "#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" do + source node['rabbitmq']['package'] + action :create_if_missing + end + dpkg_package "#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb" + end + + # Configure job control + if node['rabbitmq']['job_control'] == 'upstart' + # We start with stock init.d, remove it if we're not using init.d, otherwise leave it alone + service node['rabbitmq']['service_name'] do + action [:stop] + only_if { File.exists?('/etc/init.d/rabbitmq-server') } + end + + execute 'remove rabbitmq init.d command' do + command 'update-rc.d -f rabbitmq-server remove' + end + + file '/etc/init.d/rabbitmq-server' do + action :delete + end + + template "/etc/init/#{node['rabbitmq']['service_name']}.conf" do + source 'rabbitmq.upstart.conf.erb' + owner 'root' + group 'root' + mode 0644 + variables(:max_file_descriptors => node['rabbitmq']['max_file_descriptors']) + end + + service node['rabbitmq']['service_name'] do + provider Chef::Provider::Service::Upstart + action [ :enable, :start ] + #restart_command "stop #{node['rabbitmq']['service_name']} && start #{node['rabbitmq']['service_name']}" + end + end + + ## You'll see setsid used in all the init statements in this cookbook. This + ## is because there is a problem with the stock init script in the RabbitMQ + ## debian package (at least in 2.8.2) that makes it not daemonize properly + ## when called from chef. The setsid command forces the subprocess into a state + ## where it can daemonize properly. -Kevin (thanks to Daniel DeLeo for the help) + if node['rabbitmq']['job_control'] == 'initd' + service node['rabbitmq']['service_name'] do + start_command 'setsid /etc/init.d/rabbitmq-server start' + stop_command 'setsid /etc/init.d/rabbitmq-server stop' + restart_command 'setsid /etc/init.d/rabbitmq-server restart' + status_command 'setsid /etc/init.d/rabbitmq-server status' + supports :status => true, :restart => true + action [ :enable, :start ] + end + end + +when 'rhel', 'fedora' + #This is needed since Erlang Solutions' packages provide "esl-erlang"; this package just requires "esl-erlang" and provides "erlang". + if node['erlang']['install_method'] == 'esl' + remote_file "#{Chef::Config[:file_cache_path]}/esl-erlang-compat.rpm" do + source "https://github.com/jasonmcintosh/esl-erlang-compat/blob/master/rpmbuild/RPMS/noarch/esl-erlang-compat-R14B-1.el6.noarch.rpm?raw=true" + end + rpm_package "#{Chef::Config[:file_cache_path]}/esl-erlang-compat.rpm" + end + + if node['rabbitmq']['use_distro_version'] then + package 'rabbitmq-server' + else + remote_file "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" do + source node['rabbitmq']['package'] + action :create_if_missing + end + rpm_package "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.el6.noarch.rpm" + end + + service node['rabbitmq']['service_name'] do + action [:enable, :start] + end + +when 'suse' + # rabbitmq-server-plugins needs to be first so they both get installed + # from the right repository. Otherwise, zypper will stop and ask for a + # vendor change. + package 'rabbitmq-server-plugins' + package 'rabbitmq-server' +when 'smartos' + package 'rabbitmq' + + service 'epmd' do + action :start + end + + service node['rabbitmq']['service_name'] do + action [:enable, :start] + end +end + +if node['rabbitmq']['logdir'] + directory node['rabbitmq']['logdir'] do + owner 'rabbitmq' + group 'rabbitmq' + mode '775' + recursive true + end +end + +directory node['rabbitmq']['mnesiadir'] do + owner 'rabbitmq' + group 'rabbitmq' + mode '775' + recursive true +end + +template "#{node['rabbitmq']['config_root']}/rabbitmq-env.conf" do + source 'rabbitmq-env.conf.erb' + owner 'root' + group 'root' + mode 00644 + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" +end + +template "#{node['rabbitmq']['config_root']}/rabbitmq.config" do + source 'rabbitmq.config.erb' + owner 'root' + group 'root' + mode 00644 + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" +end + +if File.exists?(node['rabbitmq']['erlang_cookie_path']) + existing_erlang_key = File.read(node['rabbitmq']['erlang_cookie_path']) +else + existing_erlang_key = '' +end + +if node['rabbitmq']['cluster'] && (node['rabbitmq']['erlang_cookie'] != existing_erlang_key) + log "stopping service[#{node['rabbitmq']['service_name']}] to change erlang_cookie" do + level :info + notifies :stop, "service[#{node['rabbitmq']['service_name']}]", :immediately + end + + template node['rabbitmq']['erlang_cookie_path'] do + source 'doterlang.cookie.erb' + owner 'rabbitmq' + group 'rabbitmq' + mode 00400 + notifies :start, "service[#{node['rabbitmq']['service_name']}]", :immediately + notifies :run, "execute[reset-node]", :immediately + end + + # Need to reset for clustering # + execute "reset-node" do + command "rabbitmqctl stop_app && rabbitmqctl reset && rabbitmqctl start_app" + action :nothing + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb b/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb new file mode 100644 index 0000000..6c62ce3 --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/mgmt_console.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: rabbitmq +# Recipe:: mgmt_console +# +# Copyright 2012, Tacit Knowledge, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rabbitmq::default" + +plugins = %w( rabbitmq_management rabbitmq_management_visualiser ) + +service_name = node['rabbitmq']['service_name'] + +plugins.each do |plugin| + rabbitmq_plugin plugin do + action :enable + notifies :restart, "service[#{service_name}]" + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/plugin_management.rb b/chef/cookbooks/rabbitmq/recipes/plugin_management.rb new file mode 100644 index 0000000..2ea3711 --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/plugin_management.rb @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# Cookbook Name:: rabbitmq +# Recipe:: plugin_management +# +# Copyright 2013, Grégoire Seux +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rabbitmq::default" + +node['rabbitmq']['enabled_plugins'].each do |plugin| + rabbitmq_plugin plugin do + action :enable + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end + +node['rabbitmq']['disabled_plugins'].each do |plugin| + rabbitmq_plugin plugin do + action :disable + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/policy_management.rb b/chef/cookbooks/rabbitmq/recipes/policy_management.rb new file mode 100644 index 0000000..668ebb9 --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/policy_management.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: rabbitmq +# Recipe:: policy_management +# +# Author: Robert Choi +# Copyright 2013 by Robert Choi +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rabbitmq::default" + +node['rabbitmq']['policies'].each do |name, policy| + rabbitmq_policy name do + pattern policy['pattern'] + params policy['params'] + priority policy['priority'] + vhost policy['vhost'] + action :set + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end + +node['rabbitmq']['disabled_policies'].each do |policy| + rabbitmq_policy policy do + action :clear + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/user_management.rb b/chef/cookbooks/rabbitmq/recipes/user_management.rb new file mode 100644 index 0000000..9e71b0a --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/user_management.rb @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# +# Cookbook Name:: rabbitmq +# Recipe:: user_management +# +# Copyright 2013, Grégoire Seux +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rabbitmq::default" +include_recipe "rabbitmq::virtualhost_management" + +node['rabbitmq']['enabled_users'].each do |user| + rabbitmq_user user['name'] do + password user['password'] + action :add + end + rabbitmq_user user['name'] do + tag user['tag'] + action :set_tags + end + user['rights'].each do |r| + rabbitmq_user user['name'] do + vhost r['vhost'] + permissions "#{r['conf']} #{r['write']} #{r['read']}" + action :set_permissions + end + end +end + +node['rabbitmq']['disabled_users'].each do |user| + rabbitmq_user user do + action :delete + end +end diff --git a/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb b/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb new file mode 100644 index 0000000..dc90154 --- /dev/null +++ b/chef/cookbooks/rabbitmq/recipes/virtualhost_management.rb @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# Cookbook Name:: rabbitmq +# Recipe:: virtualhost_management +# +# Copyright 2013, Grégoire Seux +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rabbitmq::default" + +node['rabbitmq']['virtualhosts'].each do |virtualhost| + rabbitmq_vhost virtualhost do + action :add + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end + +node['rabbitmq']['disabled_virtualhosts'].each do |virtualhost| + rabbitmq_vhost virtualhost do + action :delete + notifies :restart, "service[#{node['rabbitmq']['service_name']}]" + end +end + diff --git a/chef/cookbooks/rabbitmq/resources/plugin.rb b/chef/cookbooks/rabbitmq/resources/plugin.rb new file mode 100644 index 0000000..4980c7a --- /dev/null +++ b/chef/cookbooks/rabbitmq/resources/plugin.rb @@ -0,0 +1,23 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: plugin +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :enable, :disable +default_action :enable + +attribute :plugin, :kind_of => String, :name_attribute => true diff --git a/chef/cookbooks/rabbitmq/resources/policy.rb b/chef/cookbooks/rabbitmq/resources/policy.rb new file mode 100644 index 0000000..5f63a97 --- /dev/null +++ b/chef/cookbooks/rabbitmq/resources/policy.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: policy +# +# Author: Robert Choi +# Copyright 2013 by Robert Choi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :set, :clear, :list +default_action :set + +attribute :policy, :kind_of => String, :name_attribute => true +attribute :pattern, :kind_of => String +attribute :params, :kind_of => Hash +attribute :priority, :kind_of => Integer +attribute :vhost, :kind_of => String diff --git a/chef/cookbooks/rabbitmq/resources/user.rb b/chef/cookbooks/rabbitmq/resources/user.rb new file mode 100644 index 0000000..c96f687 --- /dev/null +++ b/chef/cookbooks/rabbitmq/resources/user.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: user +# +# Copyright 2011-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :delete, :set_permissions, :clear_permissions, :set_tags, :clear_tags, :change_password + +attribute :user, :kind_of => String, :name_attribute => true +attribute :password, :kind_of => String +attribute :vhost, :kind_of => String +attribute :permissions, :kind_of => String +attribute :tag, :kind_of => String + +def initialize(*args) + super + @action = :add +end diff --git a/chef/cookbooks/rabbitmq/resources/vhost.rb b/chef/cookbooks/rabbitmq/resources/vhost.rb new file mode 100644 index 0000000..312b04c --- /dev/null +++ b/chef/cookbooks/rabbitmq/resources/vhost.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: vhost +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :delete + +attribute :vhost, :kind_of => String, :name_attribute => true + +def initialize(*args) + super + @action = :add +end diff --git a/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb b/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb new file mode 100644 index 0000000..a142e47 --- /dev/null +++ b/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb @@ -0,0 +1 @@ +<%= node['rabbitmq']['erlang_cookie'] %> diff --git a/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb b/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb new file mode 100644 index 0000000..a92eba0 --- /dev/null +++ b/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb @@ -0,0 +1,22 @@ +### +# Generated by Chef +### + +<% if node['rabbitmq']['local_erl_networking'] -%> +NODENAME=rabbit@localhost +NODE_IP_ADDRESS=127.0.0.1 +export ERL_EPMD_ADDRESS=127.0.0.1 +<% elsif node['rabbitmq']['erl_networking_bind_address'] -%> +NODENAME=<%= node['rabbitmq']['nodename'] %> +NODE_IP_ADDRESS=<%= node['rabbitmq']['erl_networking_bind_address'] %> +export ERL_EPMD_ADDRESS=<%= node['rabbitmq']['erl_networking_bind_address'] %> +<% else -%> +<% if node['rabbitmq']['address'] -%>NODE_IP_ADDRESS=<%= node['rabbitmq']['address'] %><% end %> +<% if node['rabbitmq']['nodename'] -%>NODENAME=<%= node['rabbitmq']['nodename'] %><% end %> +<% end -%> +<% if node['rabbitmq']['port'] -%>NODE_PORT=<%= node['rabbitmq']['port'] %><% end %> +<% if node['rabbitmq']['config'] -%>CONFIG_FILE=<%= node['rabbitmq']['config'] %><% end %> +<% if node['rabbitmq']['logdir'] -%>LOG_BASE=<%= node['rabbitmq']['logdir'] %><% end %> +<% if node['rabbitmq']['mnesiadir'] -%>MNESIA_BASE=<%= node['rabbitmq']['mnesiadir'] %><% end %> + +<% if node['rabbitmq']['open_file_limit'] -%>ulimit -n <%= node['rabbitmq']['open_file_limit'] %><% end %> diff --git a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb new file mode 100644 index 0000000..e47d34e --- /dev/null +++ b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.config.erb @@ -0,0 +1,38 @@ +%%% +%% Generated by Chef +%%% + +[ +<% if node['rabbitmq']['local_erl_networking'] %> + {kernel, [{inet_dist_use_interface,{127,0,0,1}}]}, +<% elsif node['rabbitmq']['erl_networking_bind_address'] -%> + {kernel, [{inet_dist_use_interface,{<%= node['rabbitmq']['erl_networking_bind_address'].gsub(/\./, ',') %>}}]}, +<% end %> + {rabbit, [ +<% if node['rabbitmq']['cluster'] && node['rabbitmq']['cluster_disk_nodes'] -%> + {cluster_nodes, [<%= node['rabbitmq']['cluster_disk_nodes'].map{|n| "\'#{n}\'"}.join(',') %>]}, +<% end %> +<% if node['rabbitmq']['ssl'] -%> + {ssl_listeners, [<%= node['rabbitmq']['ssl_port'] %>]}, + {ssl_options, [{cacertfile,"<%= node['rabbitmq']['ssl_cacert'] %>"}, + {certfile,"<%= node['rabbitmq']['ssl_cert'] %>"}, + {keyfile,"<%= node['rabbitmq']['ssl_key'] %>"}, + {verify,<%= node['rabbitmq']['ssl_verify'] %>}, + {fail_if_no_peer_cert,<%= node['rabbitmq']['ssl_fail_if_no_peer_cert'] %>}]}, +<% end %> + {tcp_listen_options, [binary, {packet,<%= node['rabbitmq']['tcp_listen_packet'] %>}, + {reuseaddr,<%= node['rabbitmq']['tcp_listen_reuseaddr'] %>}, + {backlog,<%= node['rabbitmq']['tcp_listen_backlog'] %>}, + {nodelay,<%= node['rabbitmq']['tcp_listen_nodelay'] %>}, + {exit_on_close,<%= node['rabbitmq']['tcp_listen_exit_on_close'] %>}, + {keepalive,<%= node['rabbitmq']['tcp_listen_keepalive'] %>}]}, +<% if node['rabbitmq']['disk_free_limit_relative'] -%> + {disk_free_limit, {mem_relative, <%= node['rabbitmq']['disk_free_limit_relative'] %>}}, +<% end %> +<% if node['rabbitmq']['vm_memory_high_watermark'] -%> + {vm_memory_high_watermark, <%= node['rabbitmq']['vm_memory_high_watermark'] %>}, +<% end %> + {default_user, <<"<%= node['rabbitmq']['default_user'] %>">>}, + {default_pass, <<"<%= node['rabbitmq']['default_pass'] %>">>} + ]} +]. diff --git a/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb new file mode 100644 index 0000000..b74ed72 --- /dev/null +++ b/chef/cookbooks/rabbitmq/templates/default/rabbitmq.upstart.conf.erb @@ -0,0 +1,11 @@ +description "Start rabbitmq on startup" +start on started networking +limit nofile <%= @max_file_descriptors %> <%= @max_file_descriptors %> + +respawn +respawn limit 5 60 + +env HOME="" +exec /usr/sbin/rabbitmq-server > /var/log/rabbitmq/startup_log \ + 2> /var/log/rabbitmq/startup_err +post-start exec /usr/sbin/rabbitmqctl wait >/dev/null 2>&1 diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md new file mode 100644 index 0000000..d348825 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/README.md @@ -0,0 +1 @@ +This cookbook is used with test-kitchen to test the parent, rabbitmq cookbook. diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb new file mode 100644 index 0000000..0ffb839 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cluster_test.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +describe "rabbitmq_test::cluster" do + + it 'writes the erlang cookie file' do + file("/var/lib/rabbitmq/.erlang.cookie").must_exist + end + + it 'writes cluster configuration to the config file' do + file("/etc/rabbitmq/rabbitmq.conf").must_match( + /^ {cluster_nodes, [.*]},$/ + ) + end + +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb new file mode 100644 index 0000000..9fecb2c --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1684_test.rb @@ -0,0 +1,41 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +describe "rabbitmq_test::cook-1684" do + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + it 'installs rabbitmq from deb file when apt isnt used' do + unless node['platform_family'] == 'debian' + skip "Only applicable on Debian family" + end + + file("#{Chef::Config[:file_cache_path]}/rabbitmq-server_#{node['rabbitmq']['version']}-1_all.deb").must_exist && + package("rabbitmq-server").must_be_installed + end + + it 'installs rabbitmq from yum when used' do + unless node['platform_family'] == 'rhel' || node['platform_family'] == 'fedora' + skip "Only applicable on RHEL/Fedora family" + end + + rpm_path = "#{Chef::Config[:file_cache_path]}/rabbitmq-server-#{node['rabbitmq']['version']}-1.noarch.rpm" + + file(rpm_path).wont_exist && package("rabbitmq-server").must_be_installed + end + +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb new file mode 100644 index 0000000..de1087b --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-1724_test.rb @@ -0,0 +1,31 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +describe "rabbitmq_test::cook-1724" do + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + it 'doesnt use the rabbitmq apt repository' do + unless node['platform_family'] == 'debian' + skip "Only applicable on Debian family" + end + + file("/etc/apt/sources.list.d/rabbitmq-source.list").wont_exist && + package("rabbitmq-server").must_be_installed + end + +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb new file mode 100644 index 0000000..1b64b14 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/cook-2151-3489_test.rb @@ -0,0 +1,37 @@ +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "rabbitmq_test::cook-2151" do + include Helpers::RabbitMQ + + it 'includes the disk_free_limit configuration setting' do + file("#{node['rabbitmq']['config_root']}/rabbitmq.config"). + must_match /\{disk_free_limit, \{mem_relative, #{node['rabbitmq']['disk_free_limit_relative']}/ + end + + it 'includes the vm_memory_high_watermark configuration setting' do + file("#{node['rabbitmq']['config_root']}/rabbitmq.config"). + must_match /\{vm_memory_high_watermark, #{node['rabbitmq']['vm_memory_high_watermark']}/ + end + + it 'includes the open_file_limit configuration setting' do + file("#{node['rabbitmq']['config_root']}/rabbitmq-env.conf"). + must_match /(ulimit -n #{node['rabbitmq']['open_file_limit']})/ + end + +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..3694864 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/default_test.rb @@ -0,0 +1,61 @@ +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + + +describe "rabbitmq_test::default" do + include Helpers::RabbitMQ + + #packages + it 'installs the rabbitmq-server package' do + if node['rabbitmq']['use_distro_version'] + package('rabbitmq-server').must_be_installed + else + package('rabbitmq-server').must_be_installed.with(:version, '3.1.5-1') + end + end + + #directories + it 'creates the mnesia directory' do + directory(node['rabbitmq']['mnesiadir']).must_have(:mode, '775').with(:owner, 'rabbitmq').and(:group, 'rabbitmq') + end + + #file + it 'has the correct config files' do + file("#{node['rabbitmq']['config_root']}/rabbitmq-env.conf").must_exist.with(:owner, 'root').and(:group, 'root') + file("#{node['rabbitmq']['config_root']}/rabbitmq.config").must_exist.with(:owner, 'root').and(:group, 'root') + end + + # service + it 'enables & starts the rabbitmq-server service' do + service(node['rabbitmq']['service_name']).must_be_enabled unless node['rabbitmq']['job_control'] == 'upstart' + service(node['rabbitmq']['service_name']).must_be_running unless node['rabbitmq']['use_distro_version'] + end + + # accepts connections + it 'accepts AMQP connections' do + unless node['rabbitmq']['use_distro_version'] + require 'bunny' + b = Bunny.new( :host => "localhost", + :port => 5672, + :user => node['rabbitmq']['default_user'], + :pass => node['rabbitmq']['default_pass'] ) + b.start + b.stop + end + end +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb new file mode 100644 index 0000000..86a0d91 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/lwrps_test.rb @@ -0,0 +1,61 @@ +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "rabbitmq_test::lwrps" do + include Helpers::RabbitMQ + + #plugins + it 'enabled the rabbitmq_stomp plugin' do + assert(plugin_enabled?("rabbitmq_stomp")) + end + + it 'disabled the nonexistant_plugin and rabbitmq_shovel plugin' do + assert(!plugin_enabled?("rabbitmq_shovel")) + assert(!plugin_enabled?("nonexistant_plugin")) + end + + #users + it 'enabled the kitchen1 and kitchen3 users' do + assert(user_enabled?("kitchen1")) + assert(user_enabled?("kitchen3")) + end + + it 'disabled the nonexistant_user and kitchen2 users' do + assert(!user_enabled?("kitchen2")) + assert(!user_enabled?("nonexistant_user")) + end + + #policies + it 'enabled the example policies from the default attributes' do + assert(policy_enabled?("ha-all")) + assert(policy_enabled?("ha-two")) + end + + it 'disabled the nonexistant_policy' do + assert(!policy_enabled?("nonexistant_policy")) + end + + #vhosts + it 'enabled the kitchen vhost' do + assert(vhost_enabled?("kitchen")) + end + + it 'disabled the nonexistant_vhost' do + assert(!vhost_enabled?("nonexistant_vhost")) + end +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb new file mode 100644 index 0000000..68514e6 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/mgmt_console_test.rb @@ -0,0 +1,30 @@ +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "rabbitmq_test::mgmt_console" do + include Helpers::RabbitMQ + + it 'enables the rabbitmq_management plugin' do + assert(plugin_enabled?("rabbitmq_management")) + end + + it 'enables the rabbitmq_management_visualiser plugin' do + assert(plugin_enabled?("rabbitmq_management_visualiser")) + end + +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb new file mode 100644 index 0000000..74a22ca --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/ssl_test.rb @@ -0,0 +1,18 @@ +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +describe "rabbitmq_test::ssl" do +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..6806c43 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,45 @@ +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Helpers + module RabbitMQ + require 'mixlib/shellout' + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + def plugin_enabled?(plugin) + plugins = Mixlib::ShellOut.new("rabbitmq-plugins list -e '#{plugin}'").run_command + plugins.stdout =~ /(\[[Ee]\]\s#{plugin})/ + end + + def policy_enabled?(policy) + policies = Mixlib::ShellOut.new("rabbitmqctl -q list_policies").run_command + policies.stdout =~ /\t#{policy}\t/ + end + + def user_enabled?(user) + users = Mixlib::ShellOut.new("rabbitmqctl -q list_users").run_command + users.stdout =~ /(#{user}\s)/ + end + + def vhost_enabled?(vhost) + vhosts = Mixlib::ShellOut.new("rabbitmqctl -q list_vhosts").run_command + vhosts.stdout =~ /(\n#{vhost}\n)/ + end + + end +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb new file mode 100644 index 0000000..7c9ba5f --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "This cookbook is used with test-kitchen to test the parent, rabbitmq cookbook." +version "1.0.0" + +depends "rabbitmq" diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb new file mode 100644 index 0000000..1644b85 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cluster.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: cluster +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +node.set['rabbitmq']['cluster'] = true +include_recipe "rabbitmq::default" diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb new file mode 100644 index 0000000..72df691 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/cook-2151-3489.rb @@ -0,0 +1,14 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: cook-2151-3489 +# +# This recipe exists to ensure that minitest tests are run. + +include_recipe "rabbitmq::default" + +# hack to give rabbit time to spin up before the tests, it seems +# to be responding that it has started before it really has +execute "sleep 10" do + action :nothing + subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb new file mode 100644 index 0000000..ac8b3c7 --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: default +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +chef_gem "bunny" + +include_recipe "rabbitmq::default" + +# hack to give rabbit time to spin up before the tests, it seems +# to be responding that it has started before it really has +execute "sleep 10" do + action :nothing + subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb new file mode 100644 index 0000000..753b15c --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/lwrps.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: lwrps +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +chef_gem "bunny" + +include_recipe "rabbitmq::default" + +# force the rabbitmq restart now, then start testing +execute "sleep 10" do + notifies :restart, "service[#{node['rabbitmq']['service_name']}]", :immediately +end + +include_recipe "rabbitmq::plugin_management" +include_recipe "rabbitmq::virtualhost_management" +include_recipe "rabbitmq::policy_management" +include_recipe "rabbitmq::user_management" + +# can't verify it actually goes through without logging in, but at least exercise the code +rabbitmq_user 'kitchen3' do + password 'foobar' + action :change_password +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb new file mode 100644 index 0000000..ac8dd7c --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/mgmt_console.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: mgmt_console +# +# Copyright 2012-2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +chef_gem "bunny" + +include_recipe "rabbitmq::mgmt_console" + +# hack to give rabbit time to spin up before the tests, it seems +# to be responding that it has started before it really has +execute "sleep 10" do + action :nothing + subscribes :run, "service[#{node['rabbitmq']['service_name']}]", :delayed +end diff --git a/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb new file mode 100644 index 0000000..468d7ad --- /dev/null +++ b/chef/cookbooks/rabbitmq/test/cookbooks/rabbitmq_test/recipes/ssl.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: rabbitmq_test +# Recipe:: ssl +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/runit/.kitchen.yml b/chef/cookbooks/runit/.kitchen.yml new file mode 100644 index 0000000..0d79fe8 --- /dev/null +++ b/chef/cookbooks/runit/.kitchen.yml @@ -0,0 +1,56 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: 11.4.4 + +platforms: +- name: ubuntu-12.10 + driver_config: + box: opscode-ubuntu-12.10 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.10_provisionerless.box + run_list: ["recipe[apt]"] + +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: ["recipe[apt]"] + +- name: debian-6 + driver_config: + box: opscode-debian-6 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-6.0.7_provisionerless.box + run_list: ["recipe[apt]"] + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +suites: +- name: default + run_list: + - recipe[minitest-handler] + - recipe[runit] + - recipe[runit_test] + - recipe[runit-other_test] + attributes: {} + +- name: service + run_list: + - recipe[minitest-handler] + - recipe[runit] + - recipe[runit_test::service] + - recipe[runit-other_test] + attributes: {} diff --git a/chef/cookbooks/runit/Berksfile b/chef/cookbooks/runit/Berksfile new file mode 100644 index 0000000..e167601 --- /dev/null +++ b/chef/cookbooks/runit/Berksfile @@ -0,0 +1,12 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" + cookbook "yum" + cookbook "runit_test", :path => "./test/cookbooks/runit_test" + cookbook "runit-other_test", :path => "./test/cookbooks/runit-other_test" + # Future, when/if minitest support for this cookbook is added + cookbook "minitest-handler" +end diff --git a/chef/cookbooks/runit/CHANGELOG.md b/chef/cookbooks/runit/CHANGELOG.md new file mode 100644 index 0000000..23c4e56 --- /dev/null +++ b/chef/cookbooks/runit/CHANGELOG.md @@ -0,0 +1,109 @@ +runit Cookbook CHANGELOG +======================== +This file is used to list changes made in each version of the runit cookbook. + + +v1.2.0 +------ +### New Feature +- **[COOK-3243](https://tickets.opscode.com/browse/COOK-3243)** - Expose LSB init directory as a configurable + +### Bug +- **[COOK-3182](https://tickets.opscode.com/browse/COOK-3182)** - Do not hardcode rpmbuild location + +### Improvement +- **[COOK-3175](https://tickets.opscode.com/browse/COOK-3175)** - Add svlogd config file support +- **[COOK-3115](https://tickets.opscode.com/browse/COOK-3115)** - Add ability to install 'runit' package from Yum + +v1.1.6 +------ +### Bug +- [COOK-2353]: Runit does not update run template if the service is already enabled +- [COOK-3013]: Runit install fails on rhel if converge is only partially successful + +v1.1.4 +------ +### Bug +- [COOK-2549]: cannot enable_service (lwrp) on Gentoo +- [COOK-2567]: Runit doesn't start at boot in Gentoo +- [COOK-2629]: runit tests have ruby 1.9 method chaning syntax +- [COOK-2867]: On debian, runit recipe will follow symlinks from /etc/init.d, overwrite /usr/bin/sv + +v1.1.2 +------ +- [COOK-2477] - runit cookbook should enable EPEL repo for CentOS 5 +- [COOK-2545] - Runit cookbook fails on Amazon Linux +- [COOK-2322] - runit init template is broken on debian + +v1.1.0 +------ +- [COOK-2353] - Runit does not update run template if the service is already enabled +- [COOK-2497] - add :nothing to allowed actions + +v1.0.6 +------ +- [COOK-2404] - allow sending sigquit +- [COOK-2431] - gentoo - it should create the runit-start template before calling it + +v1.0.4 +------ +- [COOK-2351] - add `run_template_name` to allow alternate run script template + +v1.0.2 +------ +- [COOK-2299] - runit_service resource does not properly start a non-running service + +v1.0.0 +------ +- [COOK-2254] - (formerly CHEF-154) Convert `runit_service` definition to a service resource named `runit_service`. + +This version has some backwards incompatible changes (hence the major +version bump). It is recommended that users pin the cookbook to the +previous version where it is a dependency until this version has been +tested in a non-production environment (use version 0.16.2): + + depends "runit", "<= 0.16.2" + +If you use Chef environments, pin the version in the appropriate +environment(s). + +**Changes of note** + +1. The "runit" recipe must be included before the runit_service resource +can be used. +2. The `runit_service` definition created a separate `service` +resource for notification purposes. This is still available, but the +only actions that can be notified are `:start`, `:stop`, and `:restart`. +3. The `:enable` action blocks waiting for supervise/ok after the +service symlink is created. +4. User-controlled services should be created per the runit +documentation; see README.md for an example. +5. Some parameters in the definition have changed names in the +resource. See below. + +The following parameters in the definition are renamed in the resource +to clarify their intent. + +- directory -> sv_dir +- active_directory -> service_dir +- template_name -> use service_name (name attribute) +- nolog -> set "log" to false +- start_command -> unused (was previously in the "service" resource) +- stop_command -> unused (was previously in the "service" resource) +- restart_command -> unused (was previously in the "service" resource) + +v0.16.2 +------- +- [COOK-1576] - Do not symlink /etc/init.d/servicename to /usr/bin/sv on debian +- [COOK-1960] - default_logger still looks for sv-service-log-run template +- [COOK-2035] - runit README change + +v0.16.0 +------- +- [COOK-794] default logger and `no_log` for `runit_service` definition +- [COOK-1165] - restart functionality does not work right on Gentoo due to the wrong directory in the attributes +- [COOK-1440] - Delegate service control to normal user + +v0.15.0 +------- +- [COOK-1008] - Added parameters for names of different templates in runit diff --git a/chef/cookbooks/runit/CONTRIBUTING.md b/chef/cookbooks/runit/CONTRIBUTING.md new file mode 100644 index 0000000..3a99897 --- /dev/null +++ b/chef/cookbooks/runit/CONTRIBUTING.md @@ -0,0 +1,257 @@ +# Contributing to Opscode Cookbooks + +We are glad you want to contribute to Opscode Cookbooks! The first +step is the desire to improve the project. + +You can find the answers to additional frequently asked questions +[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). + +You can find additional information about +[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) +on the wiki as well. + +## Quick-contribute + +* Create an account on our [bug tracker](http://tickets.opscode.com) +* Sign our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) +(keep reading if you're contributing on behalf of your employer) +* Create a ticket for your change on the + [bug tracker](http://tickets.opscode.com) +* Link to your patch as a rebased git branch or pull request from the + ticket +* Resolve the ticket as fixed + +We regularly review contributions and will get back to you if we have +any suggestions or concerns. + +## The Apache License and the CLA/CCLA + +Licensing is very important to open source projects, it helps ensure +the software continues to be available under the terms that the author +desired. Chef uses the Apache 2.0 license to strike a balance between +open contribution and allowing you to use the software however you +would like to. + +The license tells you what rights you have that are provided by the +copyright holder. It is important that the contributor fully +understands what rights they are licensing and agrees to them. +Sometimes the copyright holder isn't the contributor, most often when +the contributor is doing work for a company. + +To make a good faith effort to ensure these criteria are met, Opscode +requires a Contributor License Agreement (CLA) or a Corporate +Contributor License Agreement (CCLA) for all contributions. This is +without exception due to some matters not being related to copyright +and to avoid having to continually check with our lawyers about small +patches. + +It only takes a few minutes to complete a CLA, and you retain the +copyright to your contribution. + +You can complete our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). +If you're contributing on behalf of your employer, have your employer +fill out our +[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) +instead. + +## Ticket Tracker (JIRA) + +The [ticket tracker](http://tickets.opscode.com) is the most important +documentation for the code base. It provides significant historical +information, such as: + +* Which release a bug fix is included in +* Discussion regarding the design and merits of features +* Error output to aid in finding similar bugs + +Each ticket should aim to fix one bug or add one feature. + +## Using git + +You can get a quick copy of the repository for this cookbook by +running `git clone +git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. + +For collaboration purposes, it is best if you create a Github account +and fork the repository to your own account. Once you do this you will +be able to push your changes to your Github repository for others to +see and use. + +If you have another repository in your GitHub account named the same +as the cookbook, we suggest you suffix the repository with -cookbook. + +### Branches and Commits + +You should submit your patch as a git branch named after the ticket, +such as COOK-1337. This is called a _topic branch_ and allows users to +associate a branch of code with the ticket. + +It is a best practice to have your commit message have a _summary +line_ that includes the ticket number, followed by an empty line and +then a brief description of the commit. This also helps other +contributors understand the purpose of changes to the code. + + [COOK-1757] - platform_family and style + + * use platform_family for platform checking + * update notifies syntax to "resource_type[resource_name]" instead of + resources() lookup + * COOK-692 - delete config files dropped off by packages in conf.d + * dropped debian 4 support because all other platforms have the same + values, and it is older than "old stable" debian release + +Remember that not all users use Chef in the same way or on the same +operating systems as you, so it is helpful to be clear about your use +case and change so they can understand it even when it doesn't apply +to them. + +### Github and Pull Requests + +All of Opscode's open source cookbook projects are available on +[Github](http://www.github.com/opscode-cookbooks). + +We don't require you to use Github, and we will even take patch diffs +attached to tickets on the tracker. However Github has a lot of +convenient features, such as being able to see a diff of changes +between a pull request and the main repository quickly without +downloading the branch. + +If you do choose to use a pull request, please provide a link to the +pull request from the ticket __and__ a link to the ticket from the +pull request. Because pull requests only have two states, open and +closed, we can't easily filter pull requests that are waiting for a +reply from the author for various reasons. + +### More information + +Additional help with git is available on the +[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) +wiki page. + +## Functional and Unit Tests + +This cookbook is set up to run tests under +[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It +uses minitest-chef to run integration tests after the node has been +converged to verify that the state of the node. + +Test kitchen should run completely without exception using the default +[baseboxes provided by Opscode](https://github.com/opscode/bento). +Because Test Kitchen creates VirtualBox machines and runs through +every configuration in the Kitchenfile, it may take some time for +these tests to complete. + +If your changes are only for a specific recipe, run only its +configuration with Test Kitchen. If you are adding a new recipe, or +other functionality such as a LWRP or definition, please add +appropriate tests and ensure they run with Test Kitchen. + +If any don't pass, investigate them before submitting your patch. + +Any new feature should have unit tests included with the patch with +good code coverage to help protect it from future changes. Similarly, +patches that fix a bug or regression should have a _regression test_. +Simply put, this is a test that would fail without your patch but +passes with it. The goal is to ensure this bug doesn't regress in the +future. Consider a regular expression that doesn't match a certain +pattern that it should, so you provide a patch and a test to ensure +that the part of the code that uses this regular expression works as +expected. Later another contributor may modify this regular expression +in a way that breaks your use cases. The test you wrote will fail, +signalling to them to research your ticket and use case and accounting +for it. + +If you need help writing tests, please ask on the Chef Developer's +mailing list, or the #chef-hacking IRC channel. + +## Code Review + +Opscode regularly reviews code contributions and provides suggestions +for improvement in the code itself or the implementation. + +We find contributions by searching the ticket tracker for _resolved_ +tickets with a status of _fixed_. If we have feedback we will reopen +the ticket and you should resolve it again when you've made the +changes or have a response to our feedback. When we believe the patch +is ready to be merged, we will tag the _Code Reviewed_ field with +_Reviewed_. + +Depending on the project, these tickets are then merged within a week +or two, depending on the current release cycle. + +## Release Cycle + +The versioning for Opscode Cookbook projects is X.Y.Z. + +* X is a major release, which may not be fully compatible with prior + major releases +* Y is a minor release, which adds both new features and bug fixes +* Z is a patch release, which adds just bug fixes + +A released version of a cookbook will end in an even number, e.g. +"1.2.4" or "0.8.0". When development for the next version of the +cookbook begins, the "Z" patch number is incremented to the next odd +number, however the next release of the cookbook may be a major or +minor incrementing version. + +Releases of Opscode's cookbooks are usually announced on the Chef user +mailing list. Releases of several cookbooks may be batched together +and announced on the [Opscode Blog](http://www.opscode.com/blog). + +## Working with the community + +These resources will help you learn more about Chef and connect to +other members of the Chef community: + +* [chef](http://lists.opscode.com/sympa/info/chef) and + [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing + lists +* #chef and #chef-hacking IRC channels on irc.freenode.net +* [Community Cookbook site](http://community.opscode.com) +* [Chef wiki](http://wiki.opscode.com/display/chef) +* Opscode Chef [product page](http://www.opscode.com/chef) + + +## Cookbook Contribution Do's and Don't's + +Please do include tests for your contribution. If you need help, ask +on the +[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) +or the +[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). +Not all platforms that a cookbook supports may be supported by Test +Kitchen. Please provide evidence of testing your contribution if it +isn't trivial so we don't have to duplicate effort in testing. Chef +10.14+ "doc" formatted output is sufficient. + +Please do indicate new platform (families) or platform versions in the +commit message, and update the relevant ticket. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] - Updated pool resource to correctly + delete.' + +Please do use [foodcritic](http://acrmp.github.com/foodcritic) to +lint-check the cookbook. Except FC007, it should pass all correctness +rules. FC007 is okay as long as the dependent cookbooks are *required* +for the default behavior of the cookbook, such as to support an +uncommon platform, secondary recipe, etc. + +Please do ensure that your changes do not break or modify behavior for +other platforms supported by the cookbook. For example if your changes +are for Debian, make sure that they do not break on CentOS. + +Please do not modify the version number in the metadata.rb, Opscode +will select the appropriate version based on the release cycle +information above. + +Please do not update the CHANGELOG.md for a new version. Not all +changes to a cookbook may be merged and released in the same versions. +Opscode will update the CHANGELOG.md when releasing a new version of +the cookbook. diff --git a/chef/cookbooks/runit/Gemfile b/chef/cookbooks/runit/Gemfile new file mode 100644 index 0000000..6509746 --- /dev/null +++ b/chef/cookbooks/runit/Gemfile @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +group :test do + gem 'chef' + gem 'rake' + gem 'rspec' + gem 'foodcritic' +end diff --git a/chef/cookbooks/runit/LICENSE b/chef/cookbooks/runit/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/runit/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/runit/README.md b/chef/cookbooks/runit/README.md new file mode 100644 index 0000000..7712c9a --- /dev/null +++ b/chef/cookbooks/runit/README.md @@ -0,0 +1,403 @@ +runit Cookbook +============== +Installs runit and provides the `runit_service` service resource for managing processes (services) under runit. + +This cookbook does not use runit to replace system init, nor are ther plans to do so. + +For more information about runit: + +- http://smarden.org/runit/ + + +Requirements +------------ +### Platforms +- Debian/Ubuntu +- Gentoo +- RHEL + + +Attributes +---------- +See `attributes/default.rb` for defaults generated per platform. + +- `node['runit']['sv_bin']` - Full path to the `sv` binary. +- `node['runit']['chpst_bin']` - Full path to the `chpst` binary. +- `node['runit']['service_dir']` - Full path to the default "services" directory where enabled services are linked. +- `node['runit']['sv_dir']` - Full path to the directory where service lives, which gets linked to `service_dir`. +- `node['runit']['lsb_init_dir']` - Full path to the directory where the LSB-compliant init script interface will be created. +- `node['runit']['start']` - Command to start the runsvdir service +- `node['runit']['stop]` - Command to stop the runsvdir service +- `node['runit']['reload']` - Command to reload the runsvdir service + +### Optional Attributes for RHEL systems + +- `node['runit']['use_package_from_yum']` - If `true`, attempts to install + runit without building an RPM first. This is for users who already have + the package in their own Yum repository. + + +Recipes +------- +### default +The default recipe installs runit and starts `runsvdir` to supervise the services in runit's service directory (e.g., `/etc/service`). + +On RHEL family systems, it will build the runit RPM using [Ian Meyer's runit RPM SPEC](https://github.com/imeyer/runit-rpm) unless the attribute `node['runit']['use_package_from_yum']` is set to `true`. In which case it will try and install runit through the normal package installation mechanism. + +On Debian family systems, the runit packages are maintained by the runit author, Gerrit Pape, and the recipe will use that for installation. + +On Gentoo, the runit ebuild package is installed. + + +Resource/Provider +----------------- +This cookbook has a resource, `runit_service`, for managing services under runit. This service subclasses the Chef `service` resource. + +**This resource replaces the runit_service definition. See the CHANGELOG.md file in this cookbook for breaking change information and any actions you may need to take to update cookbooks using runit_service.** + +### Actions +- **enable** - enables the service, creating the required run scripts and symlinks. This is the default action. +- **start** - starts the service with `sv start` +- **stop** - stops the service with `sv stop` +- **disable** - stops the service with `sv down` and removes the service symlink +- **restart** - restarts the service with `sv restart` +- **reload** - reloads the service with `sv force-reload` +- **once** - starts the service with `sv once`. +- **hup** - sends the `HUP` signal to the service with `sv hup` +- **cont** - sends the `CONT` signal to the service +- **term** - sends the `TERM` signal to the service +- **kill** - sends the `KILL` signal to the service +- **up** - starts the service with `sv up` +- **down** - downs the service with `sv down` +- **usr1** - sends the `USR1` signal to the service with `sv 1` +- **usr2** - sends the `USR2` signal to the service with `sv 2` + +Service management actions are taken with runit's "`sv`" program. + +Read the `sv(8)` [man page](http://smarden.org/runit/sv.8.html) for more information on the `sv` program. + +### Parameter Attributes + +The first three parameters, `sv_dir`, `service_dir`, and `sv_bin` will attempt to use the corresponding node attributes, and fall back to hardcoded default values that match the settings used on Debian platform systems. + +Many of these parameters are only used in the `:enable` action. + +- **sv_dir** - The base "service directory" for the services managed by + the resource. By default, this will attempt to use the + `node['runit']['sv_dir']` attribute, and falls back to `/etc/sv`. +- **service_dir** - The directory where services are symlinked to be + supervised by `runsvdir`. By default, this will attempt to use the + `node['runit']['service_dir']` attribute, and falls back to + `/etc/service`. +- **lsb_init_dir** - The directory where an LSB-compliant init script + interface will be created. By default, this will attempt to use the + `node['runit']['lsb_init_dir']` attribute, and falls back to + `/etc/init.d`. +- **sv_bin** - The path to the `sv` program binary. This will attempt + to use the `node['runit']['sv_bin']` attribute, and falls back to + `/usr/bin/sv`. +- **service_name** - *Name attribute*. The name of the service. This + will be used in the directory of the managed service in the + `sv_dir` and `service_dir`. +- **sv_templates** - If true, the `:enable` action will create the + service directory with the appropriate templates. Default is + `true`. Set this to `false` if the service has a package that + provides its own service directory. See __Usage__ examples. +- **options** - Options passed as variables to templates, for + compatibility with legacy runit service definition. Default is an + empty hash. +- **env** - A hash of environment variables with their values as content + used in the service's `env` directory. Default is an empty hash. +- **log** - Whether to start the service's logger with svlogd, requires + a template `sv-service_name-log-run.erb` to configure the log's run + script. Default is true. +- **default_logger** - Whether a default `log/run` script should be set + up. If true, the default content of the run script will use + `svlogd` to write logs to `/var/log/service_name`. Default is false. +- **log_size** - The maximum size a log file can grow to before it is + automatically rotated. See svlogd(8) for the default value. +- **log_num** - The maximum number of log files that will be retained + after rotation. See svlogd(8) for the default value. +- **log_min** - The minimum number of log files that will be retained + after rotation (if svlogd cannot create a new file and the minimum + has not been reached, it will block). Default is no minimum. +- **log_timeout** - The maximum age a log file can get to before it is + automatically rotated, whether it has reached `log_size` or not. + Default is no timeout. +- **log_processor** - A string containing a path to a program that + rotated log files will be fed through. See the **PROCESSOR** section + of svlogd(8) for details. Default is no processor. +- **log_socket** - An string containing an IP:port pair identifying a UDP + socket that log lines will be copied to. Default is none. +- **log_prefix** - A string that will be prepended to each line as it + is logged. Default is no prefix. +- **log_config_append** - A string containing optional additional lines to add + to the log service configuration. See svlogd(8) for more details. +- **cookbook** - A cookbook where templates are located instead of + where the resource is used. Applies for all the templates in the + `enable` action. +- **finish** - whether the service has a finish script, requires a + template `sv-service_name-finish.erb` +- **control** - An array of signals to customize control of the service, + see [runsv man page](http://smarden.org/runit/runsv.8.html) on how + to use this. This requires that each template be created with the + name `sv-service_name-signal.erb`. +- **owner** - user that should own the templates created to enable the + service +- **group** - group that should own the templates created to enable the + service +- **run_template_name** - alternate filename of the run run script to + use replacing `service_name`. +- **log_template_name** - alternate filename of the log run script to + use replacing `service_name`. +- **finish_script_template_name** - alternate filename of the finish + script to use, replacing `service_name`. +- **control_template_names** - a hash of control signals (see *control* + above) and their alternate template name(s) replacing + `service_name`. +- **status_command** - The command used to check the status of the + service to see if it is enabled/running (if it's running, it's + enabled). This hardcodes the location of the sv program to + `/usr/bin/sv` due to the aforementioned cookbook load order. +- **restart_on_update** - Whether the service should be restarted when + the run script is updated. Defaults to `true`. Set to `false` if + the service shouldn't be restarted when the run script is updated. + +Unlike previous versions of the cookbook using the `runit_service` definition, the `runit_service` resource can be notified. See __Usage__ examples below. + + +Usage +----- +To get runit installed on supported platforms, use `recipe[runit]`. Once it is installed, use the `runit_service` resource to set up services to be managed by runit. + +In order to use the `runit_service` resource in your cookbook(s), each service managed will also need to have `sv-service_name-run.erb` and `sv-service_name-log-run.erb` templates created. If the `log` parameter is false, the log run script isn't created. If the `log` parameter is true, and `default_logger` is also true, the log run +script will be created with the default content: + +```bash +#!/bin/sh +exec svlogd -tt /var/log/service_name +``` + +### Examples +These are example use cases of the `runit_service` resource described above. There are others in the `runit_test` cookbook that is included in the [git repository](https://github.com/opscode-cookbooks/runit). + +**Default Example** + +This example uses all the defaults in the `:enable` action to set up the service. + +We'll set up `chef-client` to run as a service under runit, such as is done in the `chef-client` cookbook. This example will be more simple than in that cookbook. First, create the required run template, `chef-client/templates/default/sv-chef-client-run.erb`. + +```bash +#!/bin/sh +exec 2>&1 +exec /usr/bin/env chef-client -i 1800 -s 30 +``` + +Then create the required log/run template, `chef-client/templates/default/sv-chef-client-log-run.erb`. + +```bash +#!/bin/sh +exec svlogd -tt ./main +``` + +__Note__ This will cause output of the running process to go to `/etc/sv/chef-client/log/main/current`. Some people may not like this, see the following example. This is preserved for compatibility reasons. + +Finally, set up the service in the recipe with: + +```ruby +runit_service "chef-client" +``` + +**Default Logger Example** + +To use a default logger with svlogd which will log to `/var/log/chef-client/current`, instead, use the `default_logger` option. + +```ruby +runit_service "chef-client" do + default_logger true +end +``` + +**No Log Service** + +If there isn't an appendant log service, set `log` to false, and the log/run script won't be created. + +```ruby +runit_service "no-svlog" do + log false +end +``` + +**Finish Script** + +To create a service that has a finish script in its service directory, set the `finish` parameter to `true`, and create a `sv-finisher-finish.erb` template. + +```ruby +runit_service "finisher" do + finish true +end +``` + +This will create `/etc/sv/finisher/finish`. + +**Alternate service directory** + +If the service directory for the managed service isn't the `sv_dir` (`/etc/sv`), then specify it: + +```ruby +runit_service "custom_service" do + sv_dir "/etc/custom_service/runit" +end +``` + +**No Service Directory** + +If the service to manage has a package that provides its service directory, such as `git-daemon` on Debian systems, set `sv_templates` to false. + +```ruby +package "git-daemon-run" + +runit_service "git-daemon" do + sv_templates false +end +``` + +This will create the service symlink in `/etc/service`, but it will not manage any templates in the service directory. + +**User Controlled Services** + +To set up services controlled by a non-privileged user, we follow the recommended configuration in the [runit documentation](http://smarden.org/runit/faq.html#user) (Is it possible to allow a user other than root to control a service?). + +Suppose the user's name is floyd, and floyd wants to run floyds-app. Assuming that the floyd user and group are already managed with Chef, create a `runsvdir-floyd` runit_service. + +```ruby +runit_service "runsvdir-floyd" +``` + +Create the `sv-runsvdir-floyd-log-run.erb` template, or add `log false`. Also create the `sv-runsvdir-floyd-run.erb` with the following content: + +```bash +#!/bin/sh +exec 2>&1 +exec chpst -ufloyd runsvdir /home/floyd/service +``` + +Next, create the `runit_service` resource for floyd's app: + +```ruby +runit_service "floyds-app" do + sv_dir "/home/floyd/sv" + service_dir "/home/floyd/service" + owner "floyd" + group "floyd" +end +``` + +And now floyd can manage the service with sv: + +```text +$ id +uid=1000(floyd) gid=1001(floyd) groups=1001(floyd) +$ sv stop /home/floyd/service/floyds-app/ +ok: down: /home/floyd/service/floyds-app/: 0s, normally up +$ sv start /home/floyd/service/floyds-app/ +ok: run: /home/floyd/service/floyds-app/: (pid 5287) 0s +$ sv status /home/floyd/service/floyds-app/ +run: /home/floyd/service/floyds-app/: (pid 5287) 13s; run: log: (pid 4691) 726s +``` + +**Options** + +Next, let's set up memcached under runit with some additional options using the `options` parameter. First, the `memcached/templates/default/sv-memcached-run.erb` template: + +```bash +#!/bin/sh +exec 2>&1 +exec chpst -u <%= @options[:user] %> /usr/bin/memcached -v -m <%= @options[:memory] %> -p <%= @options[:port] %> +``` + +Note that the script uses `chpst` (which comes with runit) to set the user option, then starts memcached on the specified memory and port (see below). + +The log/run template, `memcached/templates/default/sv-memcached-log-run.erb`: + +```bash +#!/bin/sh +exec svlogd -tt ./main +``` + +Finally, the `runit_service` in our recipe: + +```ruby +runit_service "memcached" do + options({ + :memory => node[:memcached][:memory], + :port => node[:memcached][:port], + :user => node[:memcached][:user]}.merge(params) + ) +end +``` + +This is where the user, port and memory options used in the run template are used. + +**Notifying Runit Services** + +In previous versions of this cookbook where the definition was used, it created a `service` resource that could be notified. With the `runit_service` resource, recipes need to use the full resource name. + +For example: + +```ruby +runit_service "my-service" + +template "/etc/my-service.conf" do + notifies :restart, "runit_service[my-service]" +end +``` + +Because the resource implements actions for various commands that `sv` can send to the service, any of those actions could be used for notification. For example, `chef-client` supports triggering a Chef run with a USR1 signal. + +```ruby +template "/tmp/chef-notifier" do + notifies :usr1, "runit_service[chef-client]" +end +``` + +For older implementations of services that used `runit_service` as a definition, but may support alternate service styles, use a conditional, such as based on an attribute: + +```ruby +service_to_notify = case node['nginx']['init_style'] + when "runit" + "runit_service[nginx]" + else + "service[nginx]" + end + +template "/etc/nginx/nginx.conf" do + notifies :restart, service_to_notify +end +``` + +**More Examples** + +For more examples, see the `runit_test` cookbook's `service` recipe in the [git repository](https://github.com/opscode-cookbooks/runit). + + +License & Authors +----------------- +- Author:: Adam Jacob +- Author:: Joshua Timberman + +```text +Copyright:: 2008-2013, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/runit/Rakefile b/chef/cookbooks/runit/Rakefile new file mode 100644 index 0000000..4df2694 --- /dev/null +++ b/chef/cookbooks/runit/Rakefile @@ -0,0 +1,17 @@ +require 'rubygems' +require 'bundler' +Bundler.setup + +require 'rake' +require 'foodcritic' +require 'rspec/core/rake_task' + +task :default => [:spec] + +RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = "./test/spec{,/*/**}/*_spec.rb" +end + +FoodCritic::Rake::LintTask.new do |t| + t.options = {:fail_tags => ['correctness']} +end diff --git a/chef/cookbooks/runit/TESTING.md b/chef/cookbooks/runit/TESTING.md new file mode 100644 index 0000000..1150fef --- /dev/null +++ b/chef/cookbooks/runit/TESTING.md @@ -0,0 +1,26 @@ +Testing +======= +This cookbook has tests in the GitHub repository. To run the tests: + + git clone git://github.com/opscode-cookbooks/runit.git + cd runit + bundle install + +There are two kinds of tests, unit tests and integration tests. + +## Unit Tests + +The resource/provider code is unit tested with rspec. To run these +tests, use rake: + + bundle exec rake spec + +## Integration Tests + +Integration tests are setup to run under minitest-chef. They are +automatically run under test kitchen. + + bundle exec kitchen test + +This tests the default recipe ("default" configuration), and various +uses of the `runit_service` resource ("service" configuration). diff --git a/chef/cookbooks/runit/attributes/default.rb b/chef/cookbooks/runit/attributes/default.rb new file mode 100644 index 0000000..eed6325 --- /dev/null +++ b/chef/cookbooks/runit/attributes/default.rb @@ -0,0 +1,70 @@ +# +# Cookbook Name:: runit +# Attribute File:: sv_bin +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node["platform_family"] +when "debian" + + default["runit"]["sv_bin"] = "/usr/bin/sv" + default["runit"]["chpst_bin"] = "/usr/bin/chpst" + default["runit"]["service_dir"] = "/etc/service" + default["runit"]["sv_dir"] = "/etc/sv" + default["runit"]["lsb_init_dir"] = "/etc/init.d" + default["runit"]["executable"] = "/sbin/runit" + + if node["platform"] == "debian" + + default["runit"]["start"] = "runsvdir-start" + default["runit"]["stop"] = "" + default["runit"]["reload"] = "" + + elsif node["platform"] == "ubuntu" + + default["runit"]["start"] = "start runsvdir" + default["runit"]["stop"] = "stop runsvdir" + default["runit"]["reload"] = "reload runsvdir" + + end + +when "rhel" + + default["runit"]["sv_bin"] = "/sbin/sv" + default["runit"]["chpst_bin"] = "/sbin/chpst" + default["runit"]["service_dir"] = "/etc/service" + default["runit"]["sv_dir"] = "/etc/sv" + default["runit"]["lsb_init_dir"] = "/etc/init.d" + default["runit"]["executable"] = "/sbin/runit" + default["runit"]["use_package_from_yum"] = false + + default["runit"]["start"] = "/etc/init.d/runit-start start" + default["runit"]["stop"] = "/etc/init.d/runit-start stop" + default["runit"]["reload"] = "/etc/init.d/runit-start reload" + +when "gentoo" + + default["runit"]["sv_bin"] = "/usr/bin/sv" + default["runit"]["chpst_bin"] = "/usr/bin/chpst" + default["runit"]["service_dir"] = "/var/service" + default["runit"]["sv_dir"] = "/etc/sv" + default["runit"]["lsb_init_dir"] = "/etc/init.d" + default["runit"]["executable"] = "/sbin/runit" + default["runit"]["start"] = "/etc/init.d/runit-start start" + default["runit"]["stop"] = "/etc/init.d/runit-start stop" + default["runit"]["reload"] = "/etc/init.d/runit-start reload" + +end diff --git a/chef/cookbooks/runit/files/default/runit-2.1.1.tar.gz b/chef/cookbooks/runit/files/default/runit-2.1.1.tar.gz new file mode 100644 index 0000000..9c23646 Binary files /dev/null and b/chef/cookbooks/runit/files/default/runit-2.1.1.tar.gz differ diff --git a/chef/cookbooks/runit/files/default/runit.seed b/chef/cookbooks/runit/files/default/runit.seed new file mode 100644 index 0000000..6492920 --- /dev/null +++ b/chef/cookbooks/runit/files/default/runit.seed @@ -0,0 +1 @@ +runit runit/signalinit boolean true diff --git a/chef/cookbooks/runit/files/default/runsvdir b/chef/cookbooks/runit/files/default/runsvdir new file mode 100644 index 0000000..e69de29 diff --git a/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir b/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir new file mode 100644 index 0000000..4040e34 --- /dev/null +++ b/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir @@ -0,0 +1,6 @@ +start on runlevel-2 +start on runlevel-3 +start on runlevel-4 +start on runlevel-5 +stop on shutdown +respawn /usr/sbin/runsvdir-start diff --git a/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir b/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir b/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir b/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/chef/cookbooks/runit/libraries/default.rb b/chef/cookbooks/runit/libraries/default.rb new file mode 100644 index 0000000..e69de29 diff --git a/chef/cookbooks/runit/libraries/provider_runit_service.rb b/chef/cookbooks/runit/libraries/provider_runit_service.rb new file mode 100644 index 0000000..68137a8 --- /dev/null +++ b/chef/cookbooks/runit/libraries/provider_runit_service.rb @@ -0,0 +1,481 @@ +# +# Cookbook Name:: runit +# Provider:: service +# +# Copyright 2011, Joshua Timberman +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/service' +require 'chef/provider/link' +require 'chef/resource/link' +require 'chef/provider/directory' +require 'chef/resource/directory' +require 'chef/provider/template' +require 'chef/resource/template' +require 'chef/provider/file' +require 'chef/resource/file' +require 'chef/mixin/shell_out' +require 'chef/mixin/language' + +class Chef + class Provider + class Service + class Runit < Chef::Provider::Service + include Chef::Mixin::ShellOut + + def initialize(*args) + super + @sv_dir = nil + @run_script = nil + @log_dir = nil + @log_main_dir = nil + @default_log_dir = nil + @log_run_script = nil + @log_config_file = nil + @env_dir = nil + @env_files = nil + @finish_script = nil + @control_dir = nil + @control_signal_files = nil + @lsb_init = nil + @service_link = nil + @new_resource.supports[:status] = true + end + + def load_current_resource + @current_resource = Chef::Resource::RunitService.new(new_resource.name) + @current_resource.service_name(new_resource.service_name) + + Chef::Log.debug("Checking status of service #{new_resource.service_name}") + + # verify Runit was installed properly + unless ::File.exist?(new_resource.sv_bin) && ::File.executable?(new_resource.sv_bin) + no_runit_message = "Could not locate main runit sv_bin at \"#{new_resource.sv_bin}\". " + no_runit_message << "Did you remember to install runit before declaring a \"runit_service\" resource? " + no_runit_message << "\n\nTry adding the following to the top of your recipe:\n\ninclude_recipe \"runit\"" + raise no_runit_message + end + + @current_resource.running(running?) + @current_resource.enabled(enabled?) + @current_resource + end + + # + # Chef::Provider::Service overrides + # + + def action_enable + converge_by("configure service #{@new_resource}") do + configure_service # Do this every run, even if service is already enabled and running + Chef::Log.info("#{@new_resource} configured") + end + if @current_resource.enabled + Chef::Log.debug("#{@new_resource} already enabled - nothing to do") + else + converge_by("enable service #{@new_resource}") do + enable_service + Chef::Log.info("#{@new_resource} enabled") + end + end + load_new_resource_state + @new_resource.enabled(true) + restart_service if @new_resource.restart_on_update and run_script.updated_by_last_action? + restart_log_service if @new_resource.restart_on_update and log_run_script.updated_by_last_action? + restart_log_service if @new_resource.restart_on_update and log_config_file.updated_by_last_action? + end + + def configure_service + if new_resource.sv_templates + Chef::Log.debug("Creating sv_dir for #{new_resource.service_name}") + sv_dir.run_action(:create) + Chef::Log.debug("Creating run_script for #{new_resource.service_name}") + run_script.run_action(:create) + + if new_resource.log + Chef::Log.debug("Setting up svlog for #{new_resource.service_name}") + log_dir.run_action(:create) + log_main_dir.run_action(:create) + default_log_dir.run_action(:create) if new_resource.default_logger + log_run_script.run_action(:create) + log_config_file.run_action(:create) + else + Chef::Log.debug("log not specified for #{new_resource.service_name}, continuing") + end + + unless new_resource.env.empty? + Chef::Log.debug("Setting up environment files for #{new_resource.service_name}") + env_dir.run_action(:create) + env_files.each {|file| file.run_action(:create)} + else + Chef::Log.debug("Environment not specified for #{new_resource.service_name}, continuing") + end + + if new_resource.finish + Chef::Log.debug("Creating finish script for #{new_resource.service_name}") + finish_script.run_action(:create) + else + Chef::Log.debug("Finish script not specified for #{new_resource.service_name}, continuing") + end + + unless new_resource.control.empty? + Chef::Log.debug("Creating control signal scripts for #{new_resource.service_name}") + control_dir.run_action(:create) + control_signal_files.each {|file| file.run_action(:create)} + else + Chef::Log.debug("Control signals not specified for #{new_resource.service_name}, continuing") + end + end + + Chef::Log.debug("Creating lsb_init compatible interface #{new_resource.service_name}") + lsb_init.run_action(:create) + end + + def enable_service + Chef::Log.debug("Creating symlink in service_dir for #{new_resource.service_name}") + service_link.run_action(:create) + + Chef::Log.debug("waiting until named pipe #{service_dir_name}/supervise/ok exists.") + until ::FileTest.pipe?("#{service_dir_name}/supervise/ok") do + sleep 1 + Chef::Log.debug(".") + end + + if new_resource.log + Chef::Log.debug("waiting until named pipe #{service_dir_name}/log/supervise/ok exists.") + until ::FileTest.pipe?("#{service_dir_name}/log/supervise/ok") do + sleep 1 + Chef::Log.debug(".") + end + end + end + + def disable_service + shell_out("#{new_resource.sv_bin} down #{service_dir_name}") + Chef::Log.debug("#{new_resource} down") + FileUtils.rm(service_dir_name) + Chef::Log.debug("#{new_resource} service symlink removed") + end + + def start_service + shell_out!("#{new_resource.sv_bin} start #{service_dir_name}") + end + + def stop_service + shell_out!("#{new_resource.sv_bin} stop #{service_dir_name}") + end + + def restart_service + shell_out!("#{new_resource.sv_bin} restart #{service_dir_name}") + end + + def restart_log_service + shell_out!("#{new_resource.sv_bin} restart #{service_dir_name}/log") + end + + def reload_service + shell_out!("#{new_resource.sv_bin} force-reload #{service_dir_name}") + end + + def reload_log_service + shell_out!("#{new_resource.sv_bin} force-reload #{service_dir_name}/log") + end + + # + # Addtional Runit-only actions + # + + # only take action if the service is running + [:down, :hup, :int, :term, :kill, :quit].each do |signal| + define_method "action_#{signal}".to_sym do + if @current_resource.running + runit_send_signal(signal) + else + Chef::Log.debug("#{new_resource} not running - nothing to do") + end + end + end + + # only take action if service is *not* running + [:up, :once, :cont].each do |signal| + define_method "action_#{signal}".to_sym do + if @current_resource.running + Chef::Log.debug("#{new_resource} already running - nothing to do") + else + runit_send_signal(signal) + end + end + end + + def action_usr1 + runit_send_signal(1, :usr1) + end + + def action_usr2 + runit_send_signal(2, :usr2) + end + + private + + def runit_send_signal(signal, friendly_name=nil) + friendly_name ||= signal + converge_by("send #{friendly_name} to #{new_resource}") do + shell_out!("#{new_resource.sv_bin} #{signal} #{service_dir_name}") + Chef::Log.info("#{new_resource} sent #{friendly_name}") + new_resource.updated_by_last_action(true) + end + end + + def running? + cmd = shell_out("#{new_resource.sv_bin} status #{new_resource.service_name}") + (cmd.stdout =~ /^run:/ && cmd.exitstatus == 0) + end + + def log_running? + cmd = shell_out("#{new_resource.sv_bin} status #{new_resource.service_name}/log") + (cmd.stdout =~ /^run:/ && cmd.exitstatus == 0) + end + + def enabled? + ::File.exists?(::File.join(service_dir_name, "run")) + end + + def log_service_name + ::File.join(new_resource.service_name, "log") + end + + def sv_dir_name + ::File.join(new_resource.sv_dir, new_resource.service_name) + end + + def service_dir_name + ::File.join(new_resource.service_dir, new_resource.service_name) + end + + def log_dir_name + ::File.join(new_resource.service_dir, new_resource.service_name, log) + end + + def template_cookbook + new_resource.cookbook.nil? ? new_resource.cookbook_name.to_s : new_resource.cookbook + end + + def default_logger_content + return <<-EOF +#!/bin/sh +exec svlogd -tt /var/log/#{new_resource.service_name} +EOF + end + + # + # Helper Resources + # + def sv_dir + return @sv_dir unless @sv_dir.nil? + @sv_dir = Chef::Resource::Directory.new(sv_dir_name, run_context) + @sv_dir.recursive(true) + @sv_dir.owner(new_resource.owner) + @sv_dir.group(new_resource.group) + @sv_dir.mode(00755) + @sv_dir + end + + def run_script + return @run_script unless @run_script.nil? + @run_script = Chef::Resource::Template.new(::File.join(sv_dir_name, 'run'), run_context) + @run_script.owner(new_resource.owner) + @run_script.group(new_resource.group) + @run_script.source("sv-#{new_resource.run_template_name}-run.erb") + @run_script.cookbook(template_cookbook) + @run_script.mode(00755) + if new_resource.options.respond_to?(:has_key?) + @run_script.variables(:options => new_resource.options) + end + @run_script + end + + def log_dir + return @log_dir unless @log_dir.nil? + @log_dir = Chef::Resource::Directory.new(::File.join(sv_dir_name, 'log'), run_context) + @log_dir.recursive(true) + @log_dir.owner(new_resource.owner) + @log_dir.group(new_resource.group) + @log_dir.mode(00755) + @log_dir + end + + def log_main_dir + return @log_main_dir unless @log_main_dir.nil? + @log_main_dir = Chef::Resource::Directory.new(::File.join(sv_dir_name, 'log', 'main'), run_context) + @log_main_dir.recursive(true) + @log_main_dir.owner(new_resource.owner) + @log_main_dir.group(new_resource.group) + @log_main_dir.mode(00755) + @log_main_dir + end + + def default_log_dir + return @default_log_dir unless @default_log_dir.nil? + @default_log_dir = Chef::Resource::Directory.new(::File.join("/var/log/#{new_resource.service_name}"), run_context) + @default_log_dir.recursive(true) + @default_log_dir.owner(new_resource.owner) + @default_log_dir.group(new_resource.group) + @default_log_dir.mode(00755) + @default_log_dir + end + + def log_run_script + return @log_run_script unless @log_run_script.nil? + if new_resource.default_logger + @log_run_script = Chef::Resource::File.new(::File.join( sv_dir_name, + 'log', + 'run' ), + run_context) + @log_run_script.content(default_logger_content) + @log_run_script.owner(new_resource.owner) + @log_run_script.group(new_resource.group) + @log_run_script.mode(00755) + else + @log_run_script = Chef::Resource::Template.new(::File.join( sv_dir_name, + 'log', + 'run' ), + run_context) + @log_run_script.owner(new_resource.owner) + @log_run_script.group(new_resource.group) + @log_run_script.mode(00755) + @log_run_script.source("sv-#{new_resource.log_template_name}-log-run.erb") + @log_run_script.cookbook(template_cookbook) + if new_resource.options.respond_to?(:has_key?) + @log_run_script.variables(:options => new_resource.options) + end + end + @log_run_script + end + + def log_config_file + return @log_config_file unless @log_config_file.nil? + @log_config_file = Chef::Resource::Template.new(::File.join(sv_dir_name, 'log', 'config'), run_context) + @log_config_file.owner(new_resource.owner) + @log_config_file.group(new_resource.group) + @log_config_file.mode(00644) + @log_config_file.cookbook("runit") + @log_config_file.source("log-config.erb") + @log_config_file.variables({ + :size => new_resource.log_size, + :num => new_resource.log_num, + :min => new_resource.log_min, + :timeout => new_resource.log_timeout, + :processor => new_resource.log_processor, + :socket => new_resource.log_socket, + :prefix => new_resource.log_prefix, + :append => new_resource.log_config_append + }) + @log_config_file + end + + def env_dir + return @env_dir unless @env_dir.nil? + @env_dir = Chef::Resource::Directory.new(::File.join(sv_dir_name, 'env'), run_context) + @env_dir.owner(new_resource.owner) + @env_dir.group(new_resource.group) + @env_dir.mode(00755) + @env_dir + end + + def env_files + return @env_files unless @env_files.nil? + @env_files = new_resource.env.map do |var, value| + env_file = Chef::Resource::File.new(::File.join(sv_dir_name, 'env', var), run_context) + env_file.owner(new_resource.owner) + env_file.group(new_resource.group) + env_file.content(value) + env_file + end + @env_files + end + + def finish_script + return @finish_script unless @finish_script.nil? + @finish_script = Chef::Resource::Template.new(::File.join(sv_dir_name, 'finish'), run_context) + @finish_script.owner(new_resource.owner) + @finish_script.group(new_resource.group) + @finish_script.mode(00755) + @finish_script.source("sv-#{new_resource.finish_script_template_name}-finish.erb") + @finish_script.cookbook(template_cookbook) + if new_resource.options.respond_to?(:has_key?) + @finish_script.variables(:options => new_resource.options) + end + @finish_script + end + + def control_dir + return @control_dir unless @control_dir.nil? + @control_dir = Chef::Resource::Directory.new(::File.join(sv_dir_name, 'control'), run_context) + @control_dir.owner(new_resource.owner) + @control_dir.group(new_resource.group) + @control_dir.mode(00755) + @control_dir + end + + def control_signal_files + return @control_signal_files unless @control_signal_files.nil? + @control_signal_files = new_resource.control.map do |signal| + control_signal_file = Chef::Resource::Template.new(::File.join( sv_dir_name, + 'control', + signal), + run_context) + control_signal_file.owner(new_resource.owner) + control_signal_file.group(new_resource.group) + control_signal_file.mode(00755) + control_signal_file.source("sv-#{new_resource.control_template_names[signal]}-#{signal}.erb") + control_signal_file.cookbook(template_cookbook) + if new_resource.options.respond_to?(:has_key?) + control_signal_file.variables(:options => new_resource.options) + end + control_signal_file + end + @control_signal_files + end + + def lsb_init + return @lsb_init unless @lsb_init.nil? + initfile = ::File.join(new_resource.lsb_init_dir, new_resource.service_name) + if node['platform'] == 'debian' + ::File.unlink(initfile) if ::File.symlink?(initfile) + @lsb_init = Chef::Resource::Template.new(initfile, run_context) + @lsb_init.owner('root') + @lsb_init.group('root') + @lsb_init.mode(00755) + @lsb_init.cookbook('runit') + @lsb_init.source('init.d.erb') + @lsb_init.variables(:name => new_resource.service_name) + else + @lsb_init = Chef::Resource::Link.new(initfile, run_context) + @lsb_init.to(new_resource.sv_bin) + end + @lsb_init + end + + def service_link + return @service_link unless @service_link.nil? + @service_link = Chef::Resource::Link.new(::File.join(service_dir_name), run_context) + @service_link.to(sv_dir_name) + @service_link + end + end + end + end +end diff --git a/chef/cookbooks/runit/libraries/resource_runit_service.rb b/chef/cookbooks/runit/libraries/resource_runit_service.rb new file mode 100644 index 0000000..8797eff --- /dev/null +++ b/chef/cookbooks/runit/libraries/resource_runit_service.rb @@ -0,0 +1,231 @@ +# +# Cookbook Name:: runit +# Provider:: service +# +# Copyright 2011, Joshua Timberman +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/resource/service' + +class Chef + class Resource + class RunitService < Chef::Resource::Service + + def initialize(name, run_context=nil) + super + runit_node = runit_attributes_from_node(run_context) + @resource_name = :runit_service + @provider = Chef::Provider::Service::Runit + @supports = { :restart => true, :reload => true, :status => true } + @action = :enable + @allowed_actions = [:nothing, :start, :stop, :enable, :disable, :restart, :reload, :status, :once, :hup, :cont, :term, :kill, :up, :down, :usr1, :usr2] + + # sv_bin, sv_dir, service_dir and lsb_init_dir may have been set in the + # node attributes + @sv_bin = runit_node[:sv_bin] || '/usr/bin/sv' + @sv_dir = runit_node[:sv_dir] || '/etc/sv' + @service_dir = runit_node[:service_dir] || '/etc/service' + @lsb_init_dir = runit_node[:lsb_init_dir] || '/etc/init.d' + + @control = [] + @options = {} + @env = {} + @log = true + @cookbook = nil + @finish = false + @owner = nil + @group = nil + @enabled = false + @running = false + @default_logger = false + @restart_on_update = true + @run_template_name = @service_name + @log_template_name = @service_name + @finish_script_template_name = @service_name + @control_template_names = {} + @status_command = "#{@sv_bin} status #{@service_dir}" + @sv_templates = true + @log_size = nil + @log_num = nil + @log_min = nil + @log_timeout = nil + @log_processor = nil + @log_socket = nil + @log_prefix = nil + @log_config_append = nil + + # + # Backward Compat Hack + # + # This ensures a 'service' resource exists for all 'runit_service' resources. + # This should allow all recipes using the previous 'runit_service' definition to + # continue operating. + # + unless run_context.nil? + service_dir_name = ::File.join(@service_dir, @name) + @service_mirror = Chef::Resource::Service.new(name, run_context) + @service_mirror.provider(Chef::Provider::Service::Simple) + @service_mirror.supports(@supports) + @service_mirror.start_command("#{@sv_bin} start #{service_dir_name}") + @service_mirror.stop_command("#{@sv_bin} stop #{service_dir_name}") + @service_mirror.restart_command("#{@sv_bin} restart #{service_dir_name}") + @service_mirror.status_command("#{@sv_bin} status #{service_dir_name}") + @service_mirror.action(:nothing) + run_context.resource_collection.insert(@service_mirror) + end + end + + def sv_bin(arg=nil) + set_or_return(:sv_bin, arg, :kind_of => [String]) + end + + def sv_dir(arg=nil) + set_or_return(:sv_dir, arg, :kind_of => [String, FalseClass]) + end + + def service_dir(arg=nil) + set_or_return(:service_dir, arg, :kind_of => [String]) + end + + def lsb_init_dir(arg=nil) + set_or_return(:lsb_init_dir, arg, :kind_of => [String]) + end + + def control(arg=nil) + set_or_return(:control, arg, :kind_of => [Array]) + end + + def options(arg=nil) + if @env.empty? + opts = @options + else + opts = @options.merge!(:env_dir => ::File.join(@sv_dir, @service_name, 'env')) + end + set_or_return( + :options, + arg, + :kind_of => [Hash], + :default => opts + ) + end + + def env(arg=nil) + set_or_return(:env, arg, :kind_of => [Hash]) + end + + def log(arg=nil) + set_or_return(:log, arg, :kind_of => [TrueClass, FalseClass]) + end + + def cookbook(arg=nil) + set_or_return(:cookbook, arg, :kind_of => [String]) + end + + def finish(arg=nil) + set_or_return(:finish, arg, :kind_of => [TrueClass, FalseClass]) + end + + def owner(arg=nil) + set_or_return(:owner, arg, :regex => [Chef::Config[:user_valid_regex]]) + end + + def group(arg=nil) + set_or_return(:group, arg, :regex => [Chef::Config[:group_valid_regex]]) + end + + def default_logger(arg=nil) + set_or_return(:default_logger, arg, :kind_of => [TrueClass, FalseClass]) + end + + def restart_on_update(arg=nil) + set_or_return(:restart_on_update, arg, :kind_of => [TrueClass, FalseClass]) + end + + def run_template_name(arg=nil) + set_or_return(:run_template_name, arg, :kind_of => [String]) + end + alias :template_name :run_template_name + + def log_template_name(arg=nil) + set_or_return(:log_template_name, arg, :kind_of => [String]) + end + + def finish_script_template_name(arg=nil) + set_or_return(:finish_script_template_name, arg, :kind_of => [String]) + end + + def control_template_names(arg=nil) + set_or_return( + :control_template_names, + arg, + :kind_of => [Hash], + :default => set_control_template_names + ) + end + + def set_control_template_names + @control.each do |signal| + @control_template_names[signal] ||= @service_name + end + @control_template_names + end + + def sv_templates(arg=nil) + set_or_return(:sv_templates, arg, :kind_of => [TrueClass, FalseClass]) + end + + def log_size(arg=nil) + set_or_return(:log_size, arg, :kind_of => [Integer]) + end + + def log_num(arg=nil) + set_or_return(:log_num, arg, :kind_of => [Integer]) + end + + def log_min(arg=nil) + set_or_return(:log_min, arg, :kind_of => [Integer]) + end + + def log_timeout(arg=nil) + set_or_return(:log_timeout, arg, :kind_of => [Integer]) + end + + def log_processor(arg=nil) + set_or_return(:log_processor, arg, :kind_of => [String]) + end + + def log_socket(arg=nil) + set_or_return(:log_socket, arg, :kind_of => [String, Hash]) + end + + def log_prefix(arg=nil) + set_or_return(:log_prefix, arg, :kind_of => [String]) + end + + def log_config_append(arg=nil) + set_or_return(:log_config_append, arg, :kind_of => [String]) + end + + def runit_attributes_from_node(run_context) + runit_attr = if run_context && run_context.node + run_context.node[:runit] + end + runit_attr || {} + end + end + end +end diff --git a/chef/cookbooks/runit/metadata.rb b/chef/cookbooks/runit/metadata.rb new file mode 100644 index 0000000..79561ab --- /dev/null +++ b/chef/cookbooks/runit/metadata.rb @@ -0,0 +1,16 @@ +name "runit" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs runit and provides runit_service definition" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.2.1" + +recipe "runit", "Installs and configures runit" + +%w{ ubuntu debian gentoo centos redhat amazon scientific oracle enterpriseenterprise }.each do |os| + supports os +end + +depends "build-essential" +depends "yum" diff --git a/chef/cookbooks/runit/recipes/default.rb b/chef/cookbooks/runit/recipes/default.rb new file mode 100644 index 0000000..15cb0e2 --- /dev/null +++ b/chef/cookbooks/runit/recipes/default.rb @@ -0,0 +1,131 @@ +# +# Cookbook Name:: runit +# Recipe:: default +# +# Copyright 2008-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +service "runit" do + action :nothing +end + +execute "start-runsvdir" do + command value_for_platform( + "debian" => { "default" => "runsvdir-start" }, + "ubuntu" => { "default" => "start runsvdir" }, + "gentoo" => { "default" => "/etc/init.d/runit-start start" } + ) + action :nothing +end + +execute "runit-hup-init" do + command "telinit q" + only_if "grep ^SV /etc/inittab" + action :nothing +end + +case node["platform_family"] +when "rhel" + + if node['runit']['use_package_from_yum'] + package 'runit' + else + include_recipe "build-essential" + # `rpmdevtools` is in EPEL repo in EL <= 5 + include_recipe "yum::epel" if node["platform_version"].to_i <= 5 + + packages = %w{rpm-build rpmdevtools tar gzip} + packages.each do |p| + package p + end + + if node["platform_version"].to_i >= 6 + package "glibc-static" + else + package "buildsys-macros" + end + + rpm_installed = "rpm -qa | grep -q '^runit'" + cookbook_file "#{Chef::Config[:file_cache_path]}/runit-2.1.1.tar.gz" do + source "runit-2.1.1.tar.gz" + not_if rpm_installed + notifies :run, "bash[rhel_build_install]", :immediately + end + + bash "rhel_build_install" do + user "root" + cwd Chef::Config[:file_cache_path] + code <<-EOH + tar xzf runit-2.1.1.tar.gz + cd runit-2.1.1 + ./build.sh + EOH + notifies :install, "rpm_package[runit-211]", :immediately + action :run + not_if rpm_installed + end + + rpm_root_dir = `rpm --eval "%{_rpmdir}"` + rpm_package "runit-211" do + source rpm_root_dir.strip + "/runit-2.1.1.rpm" + action :nothing + end + end + +when "debian","gentoo" + + if platform?("gentoo") + template "/etc/init.d/runit-start" do + source "runit-start.sh.erb" + mode 0755 + end + + service "runit-start" do + action :nothing + end + end + + package "runit" do + action :install + if platform?("ubuntu", "debian") + response_file "runit.seed" + end + notifies value_for_platform( + "debian" => { "4.0" => :run, "default" => :nothing }, + "ubuntu" => { + "default" => :nothing, + "9.04" => :run, + "8.10" => :run, + "8.04" => :run }, + "gentoo" => { "default" => :run } + ), "execute[start-runsvdir]", :immediately + notifies value_for_platform( + "debian" => { "squeeze/sid" => :run, "default" => :nothing }, + "default" => :nothing + ), "execute[runit-hup-init]", :immediately + if platform?("gentoo") + notifies :enable, "service[runit-start]" + end + end + + if node["platform"] =~ /ubuntu/i && node["platform_version"].to_f <= 8.04 + cookbook_file "/etc/event.d/runsvdir" do + source "runsvdir" + mode 0644 + notifies :run, "execute[start-runsvdir]", :immediately + only_if do ::File.directory?("/etc/event.d") end + end + end +end diff --git a/chef/cookbooks/runit/templates/debian/init.d.erb b/chef/cookbooks/runit/templates/debian/init.d.erb new file mode 100644 index 0000000..7c664a5 --- /dev/null +++ b/chef/cookbooks/runit/templates/debian/init.d.erb @@ -0,0 +1,66 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: <%= @name %> +# Required-Start: +# Required-Stop: +# Default-Start: +# Default-Stop: +# Short-Description: initscript for runit-managed <%= @name %> service +### END INIT INFO + +# Author: Opscode, Inc. + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="runit-managed <%= @name %>" +NAME=<%= @name %> +RUNIT=/usr/bin/sv +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if runit is not installed +[ -x $RUNIT ] || exit 0 + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" + $RUNIT start $NAME + [ "$VERBOSE" != no ] && log_end_msg $? + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + $RUNIT stop $NAME + [ "$VERBOSE" != no ] && log_end_msg $? + ;; + status) + $RUNIT status $NAME && exit 0 || exit $? + ;; + reload) + [ "$VERBOSE" != no ] && log_daemon_msg "Reloading $DESC" "$NAME" + $RUNIT reload $NAME + [ "$VERBOSE" != no ] && log_end_msg $? + ;; + force-reload) + [ "$VERBOSE" != no ] && log_daemon_msg "Force reloading $DESC" "$NAME" + $RUNIT force-reload $NAME + [ "$VERBOSE" != no ] && log_end_msg $? + ;; + restart) + [ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME" + $RUNIT restart $NAME + [ "$VERBOSE" != no ] && log_end_msg $? + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|reload|force-reload|restart}" >&2 + exit 3 + ;; +esac + +: + diff --git a/chef/cookbooks/runit/templates/default/log-config.erb b/chef/cookbooks/runit/templates/default/log-config.erb new file mode 100644 index 0000000..6e33db1 --- /dev/null +++ b/chef/cookbooks/runit/templates/default/log-config.erb @@ -0,0 +1,24 @@ +<% if @size -%> +s<%= @size %> +<% end -%> +<% if @num -%> +n<%= @num %> +<% end -%> +<% if @min -%> +N<%= @min %> +<% end -%> +<% if @timeout -%> +t<%= @timeout %> +<% end -%> +<% if @processor -%> +!<%= @processor %> +<% end -%> +<% if @socket -%> +u<%= @socket %> +<% end -%> +<% if @prefix -%> +p<%= @prefix %> +<% end -%> +<% if @append -%> +<%= @append %> +<% end -%> diff --git a/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb b/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb new file mode 100644 index 0000000..a6c11b3 --- /dev/null +++ b/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb @@ -0,0 +1,32 @@ +#!/sbin/runscript +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +depend() { + after net +} + +start() { + ebegin "Starting runsvdir" + start-stop-daemon --start --exec /usr/bin/runsvdir \ + --background --make-pidfile \ + --pidfile /var/run/runsvdir.pid -- <%= node.runit.sv_dir %> + eend $? +} + +stop() { + local ret1 ret2 + ebegin "Stopping runsvdir" + start-stop-daemon --stop --oknodo --pidfile /var/run/runsvdir.pid + ret1=$? + eend ${ret1} + + ebegin "Stopping services and logging" + sv shutdown -w 10 <%= node.runit.sv_dir %>/* + ret2=$? + eend ${ret2} + + return $((ret1+ret2)) +} + diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md b/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md new file mode 100644 index 0000000..9dd93b5 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit-other_test/README.md @@ -0,0 +1 @@ +This cookbook is used with test-kitchen to test the parent, runit cookbok diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb new file mode 100644 index 0000000..08bb2da --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit-other_test/metadata.rb @@ -0,0 +1,6 @@ +name "runit-other_test" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "This cookbook is used with test-kitchen to test the parent, runit cookbok" +version "1.0.0" diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb new file mode 100644 index 0000000..0f3b141 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit-other_test/recipes/default.rb @@ -0,0 +1 @@ +# Empty recipe for test-kitchen diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb new file mode 100644 index 0000000..4d93a15 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit-other_test/templates/default/sv-other-cookbook-templates-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>1 +exec tail -f /var/log/* diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/README.md b/chef/cookbooks/runit/test/cookbooks/runit_test/README.md new file mode 100644 index 0000000..9dd93b5 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/README.md @@ -0,0 +1 @@ +This cookbook is used with test-kitchen to test the parent, runit cookbok diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..30f079b --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/default_test.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: runit_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "runit_test::default" do + include Helpers::RunitTest + + describe "packages" do + it 'has been installed' do + package("runit").must_be_installed + end + end +end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb new file mode 100644 index 0000000..c2ed599 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/service_test.rb @@ -0,0 +1,118 @@ +# +# Cookbook:: runit_test +# Minitest:: service +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "runit_test::service" do + include Helpers::RunitTest + + it 'creates a service with the defaults' do + service('plain-defaults').must_be_running + file('/etc/service/plain-defaults/run').must_exist + file('/etc/service/plain-defaults/log/run').must_exist + file('/etc/init.d/plain-defaults').must_exist + unless node['platform'] == 'gentoo' + link('/etc/service/plain-defaults').must_exist.with( + :link_type, :symbolic).and(:to, '/etc/sv/plain-defaults') + end + end + + it 'creates a service that doesnt use the svlog' do + service('no-svlog').must_be_running + directory('/etc/sv/no-svlog/log').wont_exist + end + + it 'creates a service that uses the default svlog' do + regexp = %r{#!/bin/sh\nexec svlogd -tt /var/log/default-svlog} + service('default-svlog').must_be_running + file('/etc/service/default-svlog/log/run').must_match(regexp) + end + + it 'creates a service that has a finish script' do + service('finisher').must_be_running + file('/etc/service/finisher/finish').must_exist + end + + it 'creates a service that uses env files' do + regexp = %r{\$PATH:/opt/chef/embedded/bin} + service('env-files').must_be_running + file('/etc/service/env-files/env/PATH').must_match(regexp) + end + + it 'creates a service that sets options for the templates' do + service('template-options').must_be_running + file('/etc/service/template-options/run').must_match("# Options are delicious") + end + + it 'creates a service that uses control signal files' do + service('control-signals').must_be_running + file('/etc/service/control-signals/control/u').must_match(/control signal up/) + end + + it 'creates a runsvdir service for a normal user' do + regexp = %r{exec chpst -ufloyd runsvdir /home/floyd/service} + service('runsvdir-floyd').must_be_running + file('/etc/service/runsvdir-floyd/run').must_match(regexp) + end + + it 'creates a service running by a normal user in its runsvdir' do + floyds_app = shell_out( + "#{node['runit']['sv_bin']} status /home/floyd/service/floyds-app", + :user => "floyd", + :cwd => "/home/floyd" + ) + assert floyds_app.stdout.include?('run:') + file('/home/floyd/service/floyds-app/run').must_exist.with(:owner, 'floyd') + file('/home/floyd/service/floyds-app/log/run').must_exist.with(:owner, 'floyd') + file('/etc/init.d/floyds-app').must_exist + unless node['platform'] == 'gentoo' + link('/home/floyd/service/floyds-app').must_exist.with( + :link_type, :symbolic).and(:to, '/home/floyd/sv/floyds-app') + end + end + + it 'creates a service with differently named template files' do + service('yerba').must_be_running + end + + it 'creates a service with differently named run script template' do + service('yerba-alt').must_be_running + end + + it 'creates a service that should exist but be disabled' do + file('/etc/sv/exist-disabled/run').must_exist + unless node['platform'] == 'gentoo' + link('/etc/service/exist-disabled').wont_exist + end + end + + it 'can use templates from another cookbook' do + service('other-cookbook-templates').must_be_running + end + + it 'creates a service that has its own run scripts' do + if node['platform_family'] == 'rhel' + skip "RHEL platforms don't have a generally available package w/ runit scripts" + end + git_daemon = shell_out("#{node['runit']['sv_bin']} status /etc/service/git-daemon") + assert git_daemon.stdout.include?('run:') + link('/etc/service/git-daemon').must_exist.with( + :link_type, :symbolic).and(:to, '/etc/sv/git-daemon') + end +end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..148315e --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: runit_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' + +module Helpers + module RunitTest + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + include Chef::Mixin::ShellOut + end +end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb new file mode 100644 index 0000000..07bc0b3 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/metadata.rb @@ -0,0 +1,6 @@ +name "runit_test" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "This cookbook is used with test-kitchen to test the parent, runit cookbok" +version "1.0.0" diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb new file mode 100644 index 0000000..2e01c15 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: runit_test +# Recipe:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "runit::default" diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb new file mode 100644 index 0000000..f3b9ec8 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/recipes/service.rb @@ -0,0 +1,153 @@ +# +# Cookbook Name:: runit_test +# Recipe:: service +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "runit::default" + +package "netcat" do + package_name "nc" if platform_family?('rhel', 'fedora') +end + +# Create a normal user to run services later +group "floyd" + +user "floyd" do + comment "Floyd the App Runner" + gid "floyd" + shell "/bin/bash" + home "/home/floyd" + manage_home true + supports :manage_home => true +end + +["sv", "service"].each do |dir| + + directory "/home/floyd/#{dir}" do + owner "floyd" + group "floyd" + recursive true + end + +end + +# Create a service with all the fixin's +runit_service "plain-defaults" + +# Create a service that doesn't use the svlog +runit_service "no-svlog" do + log false +end + +# Create a service that uses the default svlog +runit_service "default-svlog" do + default_logger true +end + +# Create a service that has a finish script +runit_service "finisher" do + finish true +end + +# Create a service that uses env files +runit_service "env-files" do + env({"PATH" => "$PATH:/opt/chef/embedded/bin"}) +end + +# Create a service that sets options for the templates +runit_service "template-options" do + options({:raspberry => "delicious"}) +end + +# Create a service that uses control signal files +runit_service "control-signals" do + control ["u"] +end + +# Create a runsvdir service for a normal user +runit_service "runsvdir-floyd" + +# # Create a service running by a normal user in its runsvdir +runit_service "floyds-app" do + sv_dir "/home/floyd/sv" + service_dir "/home/floyd/service" + owner "floyd" + group "floyd" +end + +# Create a service with differently named template files +runit_service "yerba" do + log_template_name "yerba-matte" + finish_script_template_name "yerba-matte" +end + +runit_service "yerba-alt" do + run_template_name "calabash" + default_logger true +end + +# Note: this won't update the run script for the above due to +# http://tickets.opscode.com/browse/COOK-2353 +# runit_service "the other name for yerba-alt" do +# service_name "yerba-alt" +# default_logger true +# end + +# Create a service that should exist but be disabled +runit_service "exist-disabled" + +log "Created the exist-disabled service, now disable it" + +runit_service "exist-disabled" do + action :disable +end + +runit_service "other-cookbook-templates" do + cookbook "runit-other_test" +end + +unless platform_family?("rhel") + # Create a service that has a package with its own service directory + package "git-daemon-run" + + runit_service "git-daemon" do + sv_templates false + end +end + +# Despite waiting for runit to create supervise/ok, sometimes services +# are supervised, but not actually fully started +ruby_block "sleep 5s to allow services to be fully started" do + block do + sleep 5 + end +end + +# Notify the plain defaults service as a normal service resource +file "/tmp/notifier" do + content Time.now.to_s + notifies :restart, 'service[plain-defaults]', :immediately +end + +# Test for COOK-2867 +link "/etc/init.d/cook-2867" do + to "/usr/bin/sv" +end + +runit_service "cook-2867" do + default_logger true +end diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb new file mode 100644 index 0000000..09d47e9 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-calabash-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6712 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb new file mode 100644 index 0000000..cfc0908 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6700 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb new file mode 100644 index 0000000..4427fdf --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-control-signals-u.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec echo "control signal up" >> /tmp/control-signals-up.out diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb new file mode 100644 index 0000000..a80636a --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-cook-2867-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +exec 2>&1 +# open port for the ticket #, clever eh? +exec nc -l 2867 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb new file mode 100644 index 0000000..edd2dbc --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-default-svlog-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6701 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb new file mode 100644 index 0000000..3e005ae --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-downed-service-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6702 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb new file mode 100644 index 0000000..110fa44 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-env-files-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6703 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb new file mode 100644 index 0000000..67a5669 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-exist-disabled-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6704 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb new file mode 100644 index 0000000..8f6b1d6 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-finish.erb @@ -0,0 +1,9 @@ +#!/bin/sh +code=$1 +status=$2 + +if [ $status -ne 0 ];then + echo "Finisher failed with ${code} on <%= node['fqdn'] %>" >> /tmp/finisher +else + echo "Finisher succeeded on <%= node['fqdn'] %>" +fi diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb new file mode 100644 index 0000000..1eb9417 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-finisher-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6705 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb new file mode 100644 index 0000000..068bb5d --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-floyds-app-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6706 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb new file mode 100644 index 0000000..b642ce5 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-no-svlog-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6707 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb new file mode 100644 index 0000000..3b00c22 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-plain-defaults-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6708 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb new file mode 100644 index 0000000..1a25d1d --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-runsvdir-floyd-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec chpst -ufloyd runsvdir /home/floyd/service diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb new file mode 100644 index 0000000..141f8b1 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-template-options-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +# Options are <%= @options[:raspberry] %> +exec 2>&1 +exec nc -l 6710 diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb new file mode 100644 index 0000000..4c4c0e1 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-finish.erb @@ -0,0 +1,9 @@ +#!/bin/sh +code=$1 +status=$2 + +if [ $status -ne 0 ];then + echo "Yerba failed with ${code} on <%= node['fqdn'] %>" >> /tmp/yerba +else + echo "Yerba succeeded on <%= node['fqdn'] %>" +fi diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-matte-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb new file mode 100644 index 0000000..875726c --- /dev/null +++ b/chef/cookbooks/runit/test/cookbooks/runit_test/templates/default/sv-yerba-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec nc -l 6711 diff --git a/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb b/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb new file mode 100644 index 0000000..e653224 --- /dev/null +++ b/chef/cookbooks/runit/test/spec/libraries/provider_runit_service_spec.rb @@ -0,0 +1,523 @@ +# +# Author:: Joshua Timberman +# Author:: Seth Chisamore +# +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$:.unshift(File.join(File.dirname(__FILE__), '..')) +require 'spec_helper' + +describe Chef::Provider::Service::Runit do + + subject(:provider) { Chef::Provider::Service::Runit.new(new_resource, run_context) } + + let(:sv_bin) { "/usr/bin/sv" } + let(:service_name) { "getty.service" } + let(:service_dir) { "/etc/service" } + let(:service_dir_name) { "#{service_dir}/#{service_name}" } + let(:service_status_command) { "#{sv_bin} status #{service_name}" } + let(:run_script) { File.join(service_dir, service_name, "run") } + let(:log_run_script) { File.join(service_dir, service_name, "log", "run") } + let(:log_config_file) { File.join(service_dir, service_name, "log", "config") } + let(:node) do + node = Chef::Node.new + node.automatic['platform'] = 'ubuntu' + node.automatic['platform_version'] = '12.04' + node.set['runit']['sv_bin'] = sv_bin + node + end + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + + let(:new_resource) { Chef::Resource::RunitService.new('getty.service') } + let(:current_resource) { Chef::Resource::RunitService.new('getty.service') } + + before do + provider.stub(:load_current_resource).and_return(current_resource) + provider.new_resource = new_resource + provider.current_resource = current_resource + end + + describe "#load_current_resource" do + + before do + provider.unstub(:load_current_resource) + end + + describe "runit is not installed" do + it "raises an exception" do + lambda { provider.load_current_resource }.should raise_error + end + end + + context "runit is installed" do + + let(:status_output) { "run: #{service_name}: (pid 29018) 3s; run: log: (pid 24470) 46882s" } + + before do + File.stub(:exist?).with(sv_bin).and_return(true) + File.stub(:executable?).with(sv_bin).and_return(true) + provider.stub(:shell_out). + with(service_status_command). + and_return(mock("ouput", :stdout => status_output, :exitstatus => 0)) + provider.load_current_resource + end + + describe "parsing sv status output" do + + context "returns a pid" do + let(:status_output) { "run: #{service_name}: (pid 29018) 3s; run: log: (pid 24470) 46882s" } + + it "sets resource running state to true" do + provider.current_resource.running.should be_true + end + end + + context "returns an empty pid" do + let(:status_output) { "down: #{service_name}: 2s, normally up; run: log: (pid 24470) 46250s" } + + it "sets resource running state to false" do + provider.current_resource.running.should be_false + end + end + end + + describe "checking for service run script" do + context "service run script is present in service_dir" do + before do + File.stub(:exists?).with(run_script).and_return(true) + provider.load_current_resource + end + + it "sets resource enabled state to true" do + provider.current_resource.enabled.should be_true + end + end + + context "service run script is missing" do + before do + File.stub(:exists?).with(run_script).and_return(false) + provider.load_current_resource + end + + it "sets resource enabled state to false" do + provider.current_resource.enabled.should be_false + end + end + end + end + end + + describe "actions" do + describe "start" do + + before do + provider.current_resource.running(false) + end + + %w{start up once cont}.each do |action| + it "sends the #{action} command to the sv binary" do + provider.should_receive(:shell_out!).with("#{sv_bin} #{action} #{service_dir_name}") + provider.run_action(action.to_sym) + end + end + end + + describe 'action_usr1' do + it 'sends the usr1 signal to the sv binary' do + provider.should_receive(:shell_out!).with("#{sv_bin} 1 #{service_dir_name}") + provider.run_action(:usr1) + end + end + + describe 'action_usr2' do + it 'sends the usr2 signal to the sv binary' do + provider.should_receive(:shell_out!).with("#{sv_bin} 2 #{service_dir_name}") + provider.run_action(:usr2) + end + end + + describe 'actions that manage a running service' do + before do + provider.current_resource.running(true) + end + + %w{stop down restart hup int term kill quit}.each do |action| + it "sends the '#{action}' command to the sv binary" do + provider.should_receive(:shell_out!).with("#{sv_bin} #{action} #{service_dir_name}") + provider.run_action(action.to_sym) + end + end + + describe 'action_reload' do + it "sends the 'force-reload' command to the sv binary" do + provider.should_receive(:shell_out!).with("#{sv_bin} force-reload #{service_dir_name}") + provider.run_action(:reload) + end + end + end + + describe 'action_disable' do + before do + provider.current_resource.enabled(true) + end + + it 'disables the service by running the down command and removing the symlink' do + provider.should_receive(:shell_out).with("#{sv_bin} down #{service_dir_name}") + FileUtils.should_receive(:rm).with(service_dir_name) + provider.run_action(:disable) + end + end + + describe "action_enable" do + let(:sv_dir_name) { ::File.join(new_resource.sv_dir, new_resource.service_name) } + + before(:each) do + provider.current_resource.enabled(false) + FileTest.stub(:pipe?).with("#{service_dir_name}/supervise/ok").and_return(true) + FileTest.stub(:pipe?).with("#{service_dir_name}/log/supervise/ok").and_return(true) + end + + it 'creates the sv_dir directory' do + provider.send(:sv_dir).path.should eq(sv_dir_name) + provider.send(:sv_dir).recursive.should be_true + provider.send(:sv_dir).owner.should eq(new_resource.owner) + provider.send(:sv_dir).group.should eq(new_resource.group) + provider.send(:sv_dir).mode.should eq(00755) + end + + it 'creates the run script template' do + provider.send(:run_script).path.should eq(::File.join(sv_dir_name, 'run')) + provider.send(:run_script).owner.should eq(new_resource.owner) + provider.send(:run_script).group.should eq(new_resource.group) + provider.send(:run_script).mode.should eq(00755) + provider.send(:run_script).source.should eq("sv-#{new_resource.service_name}-run.erb") + provider.send(:run_script).cookbook.should be_empty + end + + it 'sets up the supervised log directory and run script' do + provider.send(:log_dir).path.should eq(::File.join(sv_dir_name, 'log')) + provider.send(:log_dir).recursive.should be_true + provider.send(:log_dir).owner.should eq(new_resource.owner) + provider.send(:log_dir).group.should eq(new_resource.group) + provider.send(:log_dir).mode.should eq(00755) + provider.send(:log_main_dir).path.should eq(::File.join(sv_dir_name, 'log', 'main')) + provider.send(:log_main_dir).recursive.should be_true + provider.send(:log_main_dir).owner.should eq(new_resource.owner) + provider.send(:log_main_dir).group.should eq(new_resource.group) + provider.send(:log_main_dir).mode.should eq(00755) + provider.send(:log_run_script).path.should eq(::File.join(sv_dir_name, 'log', 'run')) + provider.send(:log_run_script).owner.should eq(new_resource.owner) + provider.send(:log_run_script).group.should eq(new_resource.group) + provider.send(:log_run_script).mode.should eq(00755) + provider.send(:log_run_script).source.should eq("sv-#{new_resource.log_template_name}-log-run.erb") + provider.send(:log_run_script).cookbook.should be_empty + provider.send(:log_config_file).path.should eq(::File.join(sv_dir_name, 'log', 'config')) + provider.send(:log_config_file).owner.should eq(new_resource.owner) + provider.send(:log_config_file).group.should eq(new_resource.group) + provider.send(:log_config_file).mode.should eq(00644) + provider.send(:log_config_file).source.should eq('log-config.erb') + provider.send(:log_config_file).cookbook.should eq('runit') + end + + it 'creates log/run with default content if default_logger parameter is true' do + script_content = "exec svlogd -tt /var/log/#{new_resource.service_name}" + new_resource.default_logger(true) + provider.send(:log_run_script).path.should eq(::File.join(sv_dir_name, 'log', 'run')) + provider.send(:log_run_script).owner.should eq(new_resource.owner) + provider.send(:log_run_script).group.should eq(new_resource.group) + provider.send(:log_run_script).mode.should eq(00755) + provider.send(:log_run_script).content.should include(script_content) + provider.send(:default_log_dir).path.should eq(::File.join('/var', 'log', new_resource.service_name)) + provider.send(:default_log_dir).recursive.should be_true + provider.send(:default_log_dir).owner.should eq(new_resource.owner) + provider.send(:default_log_dir).group.should eq(new_resource.group) + provider.send(:default_log_dir).mode.should eq(00755) + end + + it 'creates env directory and files' do + provider.send(:env_dir).path.should eq(::File.join(sv_dir_name, 'env')) + provider.send(:env_dir).owner.should eq(new_resource.owner) + provider.send(:env_dir).group.should eq(new_resource.group) + provider.send(:env_dir).mode.should eq(00755) + new_resource.env({'PATH' => '$PATH:/usr/local/bin'}) + provider.send(:env_files)[0].path.should eq(::File.join(sv_dir_name, 'env', 'PATH')) + provider.send(:env_files)[0].owner.should eq(new_resource.owner) + provider.send(:env_files)[0].group.should eq(new_resource.group) + provider.send(:env_files)[0].content.should eq('$PATH:/usr/local/bin') + end + + it 'creates a finish script as a template if finish_script parameter is true' do + provider.send(:finish_script).path.should eq(::File.join(sv_dir_name, 'finish')) + provider.send(:finish_script).owner.should eq(new_resource.owner) + provider.send(:finish_script).group.should eq(new_resource.group) + provider.send(:finish_script).mode.should eq(00755) + provider.send(:finish_script).source.should eq("sv-#{new_resource.finish_script_template_name}-finish.erb") + provider.send(:finish_script).cookbook.should be_empty + end + + it 'creates control directory and signal files' do + provider.send(:control_dir).path.should eq(::File.join(sv_dir_name, 'control')) + provider.send(:control_dir).owner.should eq(new_resource.owner) + provider.send(:control_dir).group.should eq(new_resource.group) + provider.send(:control_dir).mode.should eq(00755) + new_resource.control(['s']) + provider.send(:control_signal_files)[0].path.should eq(::File.join(sv_dir_name, 'control', 's')) + provider.send(:control_signal_files)[0].owner.should eq(new_resource.owner) + provider.send(:control_signal_files)[0].group.should eq(new_resource.group) + provider.send(:control_signal_files)[0].mode.should eq(00755) + provider.send(:control_signal_files)[0].source.should eq("sv-#{new_resource.control_template_names['s']}-s.erb") + provider.send(:control_signal_files)[0].cookbook.should be_empty + end + + it 'creates a symlink for LSB script compliance unless the platform is debian' do + node.automatic['platform'] = 'not_debian' + provider.send(:lsb_init).path.should eq(::File.join('/etc', 'init.d', new_resource.service_name)) + provider.send(:lsb_init).to.should eq(sv_bin) + end + + it 'creates an init script as a template for LSB compliance if the platform is debian' do + node.automatic['platform'] = 'debian' + provider.send(:lsb_init).path.should eq(::File.join('/etc', 'init.d', new_resource.service_name)) + provider.send(:lsb_init).owner.should eq('root') + provider.send(:lsb_init).group.should eq('root') + provider.send(:lsb_init).mode.should eq(00755) + provider.send(:lsb_init).cookbook.should eq('runit') + provider.send(:lsb_init).source.should eq('init.d.erb') + provider.send(:lsb_init).variables.should have_key(:name) + provider.send(:lsb_init).variables[:name].should eq(new_resource.service_name) + end + + it 'does not create anything in the sv_dir if it is nil or false' do + current_resource.stub(:enabled).and_return(false) + new_resource.stub(:sv_templates).and_return(false) + provider.should_not_receive(:sv_dir) + provider.send(:run_script).should_not_receive(:run_action).with(:create) + provider.send(:log_run_script).should_not_receive(:run_action).with(:create) + provider.should_not_receive(:log) + provider.should_not_receive(:log_main_dir) + provider.send(:lsb_init).should_receive(:run_action).with(:create) + provider.send(:service_link).should_receive(:run_action).with(:create) + provider.run_action(:enable) + end + + it 'creates a symlink from the sv dir to the service' do + provider.send(:service_link).path.should eq(service_dir_name) + provider.send(:service_link).to.should eq(sv_dir_name) + end + + it 'enables the service with memoized resource creation methods' do + current_resource.stub(:enabled).and_return(false) + provider.send(:sv_dir).should_receive(:run_action).with(:create) + provider.send(:run_script).should_receive(:run_action).with(:create) + provider.send(:log_dir).should_receive(:run_action).with(:create) + provider.send(:log_main_dir).should_receive(:run_action).with(:create) + provider.send(:log_run_script).should_receive(:run_action).with(:create) + provider.send(:log_config_file).should_receive(:run_action).with(:create) + provider.send(:lsb_init).should_receive(:run_action).with(:create) + provider.send(:service_link).should_receive(:run_action).with(:create) + provider.run_action(:enable) + end + + describe "run_script template changes" do + before do + provider.stub(:configure_service) + provider.stub(:enable_service) + end + + context "run_script is updated" do + before { provider.send(:run_script).stub(:updated_by_last_action?).and_return(true) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "restarts the service" do + provider.should_receive(:restart_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_service) + provider.run_action(:enable) + end + end + end + + context "run script is unchanged" do + before { provider.send(:run_script).stub(:updated_by_last_action?).and_return(false) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "does not restart the service" do + provider.should_not_receive(:restart_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_service) + provider.run_action(:enable) + end + end + end + end + + describe "log_run_script template changes" do + before do + provider.stub(:configure_service) + provider.stub(:enable_service) + end + + context "log_run_script is updated" do + before { provider.send(:log_run_script).stub(:updated_by_last_action?).and_return(true) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "restarts the service" do + provider.should_receive(:restart_log_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + end + + context "log_run_script is unchanged" do + before { provider.send(:log_run_script).stub(:updated_by_last_action?).and_return(false) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + end + end + + describe "log_config_file template changes" do + before do + provider.stub(:configure_service) + provider.stub(:enable_service) + end + + context "log_config_file is updated" do + before { provider.send(:log_config_file).stub(:updated_by_last_action?).and_return(true) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "restarts the service" do + provider.should_receive(:restart_log_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + end + + context "log_config_file is unchanged" do + before { provider.send(:log_config_file).stub(:updated_by_last_action?).and_return(false) } + + context "restart_on_update attribute is true" do + before { new_resource.restart_on_update(true) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + + context "restart_on_update attribute is false" do + before { new_resource.restart_on_update(false) } + + it "does not restart the service" do + provider.should_not_receive(:restart_log_service) + provider.run_action(:enable) + end + end + end + end + + context 'new resource conditionals' do + before(:each) do + current_resource.stub(:enabled).and_return(false) + provider.send(:sv_dir).stub(:run_action).with(:create) + provider.send(:run_script).stub(:run_action).with(:create) + provider.send(:lsb_init).stub(:run_action).with(:create) + provider.send(:service_link).stub(:run_action).with(:create) + provider.send(:log_dir).stub(:run_action).with(:create) + provider.send(:log_main_dir).stub(:run_action).with(:create) + provider.send(:log_run_script).stub(:run_action).with(:create) + provider.send(:log_config_file).stub(:run_action).with(:create) + end + + it 'doesnt create the log dir or run script if log is false' do + new_resource.stub(:log).and_return(false) + provider.should_not_receive(:log) + provider.run_action(:enable) + end + + it 'creates the env dir and config files if env is set' do + new_resource.stub(:env).and_return({'PATH' => '/bin'}) + provider.send(:env_dir).should_receive(:run_action).with(:create) + provider.send(:env_files).should_receive(:each).once + provider.run_action(:enable) + end + + it 'creates the control dir and signal files if control is set' do + new_resource.stub(:control).and_return(['s', 'u']) + provider.send(:control_dir).should_receive(:run_action).with(:create) + provider.send(:control_signal_files).should_receive(:each).once + provider.run_action(:enable) + end + end + end + end +end diff --git a/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb b/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb new file mode 100644 index 0000000..d13ade9 --- /dev/null +++ b/chef/cookbooks/runit/test/spec/libraries/resource_runit_service_spec.rb @@ -0,0 +1,284 @@ +# +# Author:: Joshua Timberman +# Author:: Seth Chisamore +# +# Copyright:: Copyright (c) 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +$:.unshift(File.join(File.dirname(__FILE__), '..')) +require 'spec_helper' + +describe Chef::Resource::RunitService do + + subject(:resource) { Chef::Resource::RunitService.new(service_name, run_context) } + + let(:service_name) { 'getty.service' } + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + + its(:class) { should be Chef::Resource::RunitService } + its(:resource_name) { should eq(:runit_service)} + its(:provider) { should eq(Chef::Provider::Service::Runit) } + its(:service_name) { should eq('getty.service') } + its(:sv_dir) { should eq('/etc/sv') } + its(:sv_bin) { should eq("/usr/bin/sv") } + its(:lsb_init_dir) { should eq("/etc/init.d") } + + describe "setting supported default values from node attributes" do + let(:sv_bin) { "/fake/bin/sv_bin" } + let(:sv_dir) { "/fake/sv_dir/path" } + let(:service_dir) { "/fake/service_dir" } + let(:lsb_init_dir) { "/fake/lsb_init_dir" } + let(:node) do + node = Chef::Node.new + node.set['runit']['sv_bin'] = sv_bin + node.set['runit']['sv_dir'] = sv_dir + node.set['runit']['service_dir'] = service_dir + node.set['runit']['lsb_init_dir'] = lsb_init_dir + node + end + + its(:sv_bin) { should eq sv_bin } + its(:sv_dir) { should eq sv_dir } + its(:service_dir) { should eq service_dir } + its(:lsb_init_dir) { should eq lsb_init_dir } + end + + describe "backward compatiblility hack" do + + let(:simple_service_name) { "service[#{service_name}]" } + + it "creates a simple service with the same name" do + resource_collection = resource.run_context.resource_collection + simple_service = resource_collection.find(simple_service_name) + simple_service.to_s.should eq(simple_service_name) + simple_service.class.should be Chef::Resource::Service + simple_service.provider.should be Chef::Provider::Service::Simple + end + + end + + it 'has an sv_dir parameter that can be set' do + resource.sv_dir('/var/lib/sv') + resource.sv_dir.should eq('/var/lib/sv') + end + + it 'allows sv_dir parameter to be set false (so users can use an existing sv dir)' do + resource.sv_dir(false) + resource.sv_dir.should be_false + end + + it 'has a service_dir parameter set to /etc/service by default' do + resource.service_dir.should eq('/etc/service') + end + + it 'has a service_dir parameter that can be set' do + resource.service_dir('/var/service') + resource.service_dir.should eq('/var/service') + end + + it 'has a lsb_init_dir parameter set to /etc/init.d by default' do + resource.lsb_init_dir.should eq('/etc/init.d') + end + + it 'has a lsb_init_dir parameter that can be set' do + resource.lsb_init_dir('/other/lsb_init_dir') + resource.lsb_init_dir.should eq('/other/lsb_init_dir') + end + + it 'has a control parameter that can be set as an array of service control characters' do + resource.control(['s', 'u']) + resource.control.should eq(['s', 'u']) + end + + it 'has an options parameter that can be set as a hash of arbitrary options' do + resource.options({:binary => '/usr/bin/noodles'}) + resource.options.should have_key(:binary) + resource.options[:binary].should eq('/usr/bin/noodles') + end + + it 'has an env parameter that can be set as a hash of environment variables' do + resource.env({'PATH' => '$PATH:/usr/local/bin'}) + resource.env.should have_key('PATH') + resource.env['PATH'].should include('/usr/local/bin') + end + + it 'adds :env_dir to options if env is set' do + resource.env({'PATH' => '/bin'}) + resource.options.should have_key(:env_dir) + resource.options[:env_dir].should eq(::File.join(resource.sv_dir, resource.service_name, 'env')) + end + + it 'has a log parameter to control whether a log service is setup' do + resource.log.should be_true + end + + it 'has a log parameter that can be set to false' do + resource.log(false) + resource.log.should be_false + end + + it 'raises an exception if the log parameter is set to nil' do + resource.log(nil) + resource.log.should raise_exception + end + + it 'has a cookbook parameter that can be set' do + resource.cookbook('noodles') + resource.cookbook.should eq('noodles') + end + + it 'has a finish parameter that is false by default' do + resource.finish.should be_false + end + + it 'hash a finish parameter that controls whether a finish script is created' do + resource.finish(true) + resource.finish.should be_true + end + + it 'has an owner parameter that can be set' do + resource.owner('monkey') + resource.owner.should eq('monkey') + end + + it 'has a group parameter that can be set' do + resource.group('primates') + resource.group.should eq('primates') + end + + it 'has an enabled parameter to determine if the current resource is enabled' do + resource.enabled.should be_false + end + + it 'has a running parameter to determine if the current resource is running' do + resource.running.should be_false + end + + it 'has a default_logger parameter that is false by default' do + resource.default_logger.should be_false + end + + it 'has a default_logger parameter that controls whether a default log template should be created' do + resource.default_logger(true) + resource.default_logger.should be_true + end + + it 'has a restart_on_update parameter that is true by default' do + resource.restart_on_update.should be_true + end + + it 'has a restart_on_update parameter that controls whether a the service is restarted when the run script is updated' do + resource.restart_on_update(false) + resource.restart_on_update.should be_false + end + + it 'sets the run_template_name to the service_name by default' do + resource.run_template_name.should eq(resource.service_name) + end + + it 'sets the log_template_name to the service_name by default' do + resource.log_template_name.should eq(resource.service_name) + end + + it 'has a run_template_name parameter to allow a custom template name for the run run script' do + resource.run_template_name('foo_bar') + resource.run_template_name.should eq('foo_bar') + end + + it 'has a template_name parameter to allow a custom template name for the run run script for backwards compatiblility' do + resource.template_name('foo_baz') + resource.run_template_name.should eq('foo_baz') + end + + it 'has a log_template_name parameter to allow a custom template name for the log run script' do + resource.log_template_name('write_noodles') + resource.log_template_name.should eq('write_noodles') + end + + it 'sets the control_template_names for each control character to the service_name by default' do + resource.control(['s', 'u']) + resource.control_template_names.should have_key('s') + resource.control_template_names.should have_key('u') + resource.control_template_names['s'].should eq(resource.service_name) + resource.control_template_names['u'].should eq(resource.service_name) + end + + it 'has a control_template_names parameter to allow custom template names for the control scripts' do + resource.control_template_names({ + 's' => 'banana_start', + 'u' => 'noodle_up' + }) + resource.control_template_names.should have_key('s') + resource.control_template_names.should have_key('u') + resource.control_template_names['s'].should eq('banana_start') + resource.control_template_names['u'].should eq('noodle_up') + end + + it 'sets the finish_script_template_name to the service_name by default' do + resource.finish_script_template_name.should eq(resource.service_name) + end + + it 'has a finish_script_template_name parameter to allow a custom template name for the finish script' do + resource.finish_script_template_name('eat_bananas') + resource.finish_script_template_name.should eq('eat_bananas') + end + + it 'has a sv_templates parameter to control whether the sv_dir templates are created' do + resource.sv_templates(false) + resource.sv_templates.should be_false + end + + it "has a log_size parameter to control the maximum log size" do + resource.log_size(1000000) + resource.log_size.should eq(1000000) + end + + it "has a log_num parameter to control the maximum number of logs" do + resource.log_num(10) + resource.log_num.should eq(10) + end + + it "has a log_min parameter to control the minimum number of logs" do + resource.log_min(5) + resource.log_min.should eq(5) + end + + it "has a log_timeout parameter to control the maximum age of a log file" do + resource.log_timeout(60 * 60) + resource.log_timeout.should eq(60 * 60) + end + + it "has a log_processor parameter to allow logs to be fed through it after rotation" do + resource.log_processor("/usr/local/bin/process") + resource.log_processor.should eq("/usr/local/bin/process") + end + + it "has a log_socket parameter to allow log lines to be sent to a UDP socket" do + resource.log_socket("127.0.0.1:1514") + resource.log_socket.should eq("127.0.0.1:1514") + end + + it "has a log_prefix parameter to allow log lines to be prefixed with a fixed string" do + resource.log_prefix("myservice:") + resource.log_prefix.should eq("myservice:") + end + + it "has a log_config_append parameter to allow arbitrary configuration entries to be added to the configuration" do + resource.log_config_append("-bogus") + resource.log_config_append.should eq("-bogus") + end +end diff --git a/chef/cookbooks/runit/test/spec/spec_helper.rb b/chef/cookbooks/runit/test/spec/spec_helper.rb new file mode 100644 index 0000000..4efe8f9 --- /dev/null +++ b/chef/cookbooks/runit/test/spec/spec_helper.rb @@ -0,0 +1,28 @@ + +require 'chef/platform' +require 'chef/run_context' +require 'chef/resource' +require 'chef/resource/service' +require 'chef/provider/service/simple' +require 'chef/event_dispatch/base' +require 'chef/event_dispatch/dispatcher' + +$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "libraries")) +require 'provider_runit_service' +require 'resource_runit_service' + +RSpec.configure do |config| + # Use color in STDOUT + config.color_enabled = true + + # Use color not only in STDOUT but also in pagers and files + config.tty = true + + # Use the specified formatter + config.formatter = :documentation # :progress, :html, :textmate + + # :focus support to allow zooming in a single test/block + config.filter_run :focus => true + config.run_all_when_everything_filtered = true + config.treat_symbols_as_metadata_keys_with_true_values = true +end diff --git a/chef/cookbooks/selinux/CHANGELOG.md b/chef/cookbooks/selinux/CHANGELOG.md new file mode 100644 index 0000000..292d83d --- /dev/null +++ b/chef/cookbooks/selinux/CHANGELOG.md @@ -0,0 +1,17 @@ +## v0.5.6: + +* [COOK-2124] - enforcing recipe fails if selinux is disabled + +## v0.5.4: + +* [COOK-1277] - disabled recipe fails on systems w/o selinux installed + +## v0.5.2: + +* [COOK-789] - fix dangling commas causing syntax error on some rubies + +## v0.5.0: + +* [COOK-678] - add the selinux cookbook to the repository +* Use main selinux config file (/etc/selinux/config) +* Use getenforce instead of selinuxenabled for enforcing and permissive diff --git a/chef/cookbooks/selinux/CONTRIBUTING b/chef/cookbooks/selinux/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/selinux/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/selinux/LICENSE b/chef/cookbooks/selinux/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/selinux/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/selinux/README.md b/chef/cookbooks/selinux/README.md new file mode 100644 index 0000000..2e05b7c --- /dev/null +++ b/chef/cookbooks/selinux/README.md @@ -0,0 +1,51 @@ +Description +=========== + +Provides recipes for manipulating selinux policy enforcement + +Requirements +============ + +RHEL family distribution or other Linux system that uses SELinux. + +## Platform: + +Tested on RHEL 5.6, 6.0 and 6.1. + +Usage +===== + +SELinux is enforcing by default on RHEL family distributions, however the use of SELinux has complicated considerations when using configuration management. Often, users are recommended to set SELinux to permissive mode, or disabled completely. To ensure that SELinux is permissive or disabled, choose the appropriate recipe (`selinux::permissive`, `selinux::disabled`) and apply it to the node early in the run list. For example in a `base` role used by all RHEL systems: + + name "base" + description "Base role applied to all nodes." + run_list( + "recipe[selinux::permissive]", + ) + +Roadmap +======= + +Use a node attribute to determine which recipe to load automatically from selinux::default. + +Add LWRP/Libraries for manipulating security contexts for files and services managed by Chef. + +License and Author +================== + +Author:: Sean OMeara () +Author:: Joshua Timberman () + +Copyright:: 2011, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/selinux/metadata.rb b/chef/cookbooks/selinux/metadata.rb new file mode 100644 index 0000000..9d26a2d --- /dev/null +++ b/chef/cookbooks/selinux/metadata.rb @@ -0,0 +1,7 @@ +name "selinux" +maintainer "Opscode, Inc." +maintainer_email "someara@opscode.com" +license "Apache" +description "Installs/Configures selinux" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.5.6" diff --git a/chef/cookbooks/selinux/recipes/default.rb b/chef/cookbooks/selinux/recipes/default.rb new file mode 100644 index 0000000..ecf7912 --- /dev/null +++ b/chef/cookbooks/selinux/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: selinux +# Recipe:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/chef/cookbooks/selinux/recipes/disabled.rb b/chef/cookbooks/selinux/recipes/disabled.rb new file mode 100644 index 0000000..a47c7b1 --- /dev/null +++ b/chef/cookbooks/selinux/recipes/disabled.rb @@ -0,0 +1,35 @@ +# +# Author:: Sean OMeara () +# Cookbook Name:: selinux +# Recipe:: disabled +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +execute "disable selinux enforcement" do + only_if "which selinuxenabled && selinuxenabled" + command "setenforce 0" + action :run + notifies :create, "template[/etc/selinux/config]" +end + +template "/etc/selinux/config" do + source "sysconfig/selinux.erb" + variables( + :selinux => "disabled", + :selinuxtype => "targeted" + ) + action :nothing +end diff --git a/chef/cookbooks/selinux/recipes/enforcing.rb b/chef/cookbooks/selinux/recipes/enforcing.rb new file mode 100644 index 0000000..aa83d80 --- /dev/null +++ b/chef/cookbooks/selinux/recipes/enforcing.rb @@ -0,0 +1,33 @@ +# +# Author:: Sean OMeara () +# Cookbook Name:: selinux +# Recipe:: enforcing +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +execute "enable selinux enforcement" do + not_if "getenforce | egrep -qx 'Enforcing|Disabled'" + command "setenforce 1" + action :run +end + +template "/etc/selinux/config" do + source "sysconfig/selinux.erb" + variables( + :selinux => "enforcing", + :selinuxtype => "targeted" + ) +end diff --git a/chef/cookbooks/selinux/recipes/permissive.rb b/chef/cookbooks/selinux/recipes/permissive.rb new file mode 100644 index 0000000..aa50956 --- /dev/null +++ b/chef/cookbooks/selinux/recipes/permissive.rb @@ -0,0 +1,35 @@ +# +# Author:: Sean OMeara () +# Cookbook Name:: selinux +# Recipe:: permissive +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +execute "enable selinux as permissive" do + not_if "getenforce | egrep -qx 'Permissive|Disabled'" + command "setenforce 0" + ignore_failure true + action :run +end + +template "/etc/selinux/config" do + source "sysconfig/selinux.erb" + not_if "getenforce | grep -qx 'Disabled'" + variables( + :selinux => "permissive", + :selinuxtype => "targeted" + ) +end diff --git a/chef/cookbooks/selinux/templates/default/sysconfig/selinux.erb b/chef/cookbooks/selinux/templates/default/sysconfig/selinux.erb new file mode 100644 index 0000000..2b393f0 --- /dev/null +++ b/chef/cookbooks/selinux/templates/default/sysconfig/selinux.erb @@ -0,0 +1,11 @@ +# This file controls the state of SELinux on the system. +# SELINUX= can take one of these three values: +# enforcing - SELinux security policy is enforced. +# permissive - SELinux prints warnings instead of enforcing. +# disabled - SELinux is fully disabled. +SELINUX=<%= @selinux %> +# SELINUXTYPE= type of policy in use. Possible values are: +# targeted - Only targeted network daemons are protected. +# strict - Full SELinux protection. +SELINUXTYPE=<%= @selinuxtype %> + diff --git a/chef/cookbooks/statsd/README.rdoc b/chef/cookbooks/statsd/README.rdoc new file mode 100644 index 0000000..ec71017 --- /dev/null +++ b/chef/cookbooks/statsd/README.rdoc @@ -0,0 +1,26 @@ += DESCRIPTION: + +Installs and configures Statsd (http://github.com/etsy/statsd) + += REQUIREMENTS: + +Ubuntu 11.04 (Natty) + += ATTRIBUTES: + +* statsd/port : The port for Statsd to listen for stats on. +* statsd/graphite_host : The host to forward processed statistics to. +* statsd/graphite_port : The port to forward processed statistics to. + += USAGE: + +Include the statsd recipe, which will checkout Statsd from git, build a Debian package from it, +and then install the package. Statsd is run under a "statsd" system user. + +By default statsd will attempt to send statistics to a graphite instance running on localhost, which +can be configured using the Graphite cookbook at http://community.opscode.com/cookbooks/graphite. + += CAVEATS: + +This cookbook has only been tested on Ubuntu Natty (11.04). That is due to it using the "nodejs" package +rather then attempting to build Node from source. diff --git a/chef/cookbooks/statsd/attributes/statsd.rb b/chef/cookbooks/statsd/attributes/statsd.rb new file mode 100644 index 0000000..0df7e9d --- /dev/null +++ b/chef/cookbooks/statsd/attributes/statsd.rb @@ -0,0 +1,3 @@ +default[:statsd][:port] = 8125 +default[:statsd][:graphite_port] = 2003 +default[:statsd][:graphite_host] = "localhost" diff --git a/chef/cookbooks/statsd/files/default/statsd b/chef/cookbooks/statsd/files/default/statsd new file mode 100644 index 0000000..0075f53 --- /dev/null +++ b/chef/cookbooks/statsd/files/default/statsd @@ -0,0 +1,3 @@ +#!/bin/sh + +exec node /usr/local/statsd/stats.js /etc/statsd/config.js diff --git a/chef/cookbooks/statsd/files/default/upstart.conf b/chef/cookbooks/statsd/files/default/upstart.conf new file mode 100644 index 0000000..9eec600 --- /dev/null +++ b/chef/cookbooks/statsd/files/default/upstart.conf @@ -0,0 +1,12 @@ +description "statsd" +author "ops@simplymeasured.com" + +start on startup +stop on shutdown + +script + echo $$ > /var/run/statsd.pid + # We found $HOME is needed. Without it, we ran into problems + export HOME="/root" + exec sudo -u statsd /usr/local/sbin/statsd >> /var/log/statsd.log 2>&1 +end script diff --git a/chef/cookbooks/statsd/metadata.rb b/chef/cookbooks/statsd/metadata.rb new file mode 100644 index 0000000..2ac8b7b --- /dev/null +++ b/chef/cookbooks/statsd/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Jon Wood" +maintainer_email "jon@blankpad.net" +license "Apache 2.0" +description "Installs/Configures statsd" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1.0" + +supports "ubuntu" + +depends "build-essential" +depends "git" +depends "nodejs" diff --git a/chef/cookbooks/statsd/recipes/default.rb b/chef/cookbooks/statsd/recipes/default.rb new file mode 100644 index 0000000..3c6e098 --- /dev/null +++ b/chef/cookbooks/statsd/recipes/default.rb @@ -0,0 +1,63 @@ +# +# Cookbook Name:: statsd +# Recipe:: default +# +# Copyright 2011, Blank Pad Development +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "build-essential" +include_recipe "git" +include_recipe "nodejs" + +execute "checkout statsd" do + command "git clone git://github.com/etsy/statsd" + creates "/usr/local/statsd" + cwd "/usr/local" +end + +directory "/etc/statsd" + +template "/etc/statsd/config.js" do + source "config.js.erb" + mode 0644 + variables( + :port => node[:statsd][:port], + :graphitePort => node[:statsd][:graphite_port], + :graphiteHost => node[:statsd][:graphite_host] + ) + + notifies :restart, "service[statsd]" +end + +cookbook_file "/usr/local/sbin/statsd" do + source "statsd" + mode 0755 +end + +cookbook_file "/etc/init/statsd.conf" do + source "upstart.conf" + mode 0644 +end + +user "statsd" do + comment "statsd" + system true + shell "/bin/false" +end + +service "statsd" do + provider Chef::Provider::Service::Upstart + action [ :enable, :start ] +end diff --git a/chef/cookbooks/statsd/templates/default/config.js.erb b/chef/cookbooks/statsd/templates/default/config.js.erb new file mode 100644 index 0000000..c4afa71 --- /dev/null +++ b/chef/cookbooks/statsd/templates/default/config.js.erb @@ -0,0 +1,5 @@ +{ +graphitePort: <%= @graphitePort %> +, graphiteHost: "<%= @graphiteHost %>" +, port: <%= @port %> +} diff --git a/chef/cookbooks/sysctl/.tailor b/chef/cookbooks/sysctl/.tailor new file mode 100644 index 0000000..48524d0 --- /dev/null +++ b/chef/cookbooks/sysctl/.tailor @@ -0,0 +1,14 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +# encoding: utf-8 + +Tailor.config do |config| + config.formatters "text" + config.file_set "**/*.rb" + + config.file_set "Gemfile", :config do |style| + style.spaces_after_comma 1, level: :off + style.max_line_length 120 + end + config.file_sets[:config].update_file_list("Berksfile") +end diff --git a/chef/cookbooks/sysctl/README.md b/chef/cookbooks/sysctl/README.md new file mode 100644 index 0000000..adfeeda --- /dev/null +++ b/chef/cookbooks/sysctl/README.md @@ -0,0 +1,114 @@ +Support +======= + +Issues have been disabled for this repository. +Any issues with this cookbook should be raised here: + +[https://github.com/rcbops/chef-cookbooks/issues](https://github.com/rcbops/chef-cookbooks/issues) + +Please title the issue as follows: + +[sysctl]: \ + +In the issue description, please include a longer description of the issue, along with any relevant log/command/error output. +If logfiles are extremely long, please place the relevant portion into the issue description, and link to a gist containing the entire logfile + + +Description +=========== + +Set sysctl values from Chef! + +Attributes +========== + +* `node['sysctl']` - A namespace for sysctl settings. + +Usage +===== + +There are two ways of setting sysctl values: + +1. Set chef attributes in the **sysctl** namespace. e.g.: + + node.set['sysctl']['set swappiness'] = { 'vm.swappiness' => '20' } +2. Set values in a `cookbook_file` Resource. +3. With Ressource/Provider. + +Resource/Provider +================= + +This Cookbook includes two LWRPs: + +1. **sysctl** +2. **sysctl_multi** + +sysctl +------ + +## Actions + +- **:save** - Save and set a sysctl value (default). +- **:set** - Set a sysctl value. +- **:remove** - Remove a (previous set) sysctl. + +## Attribute Parameters + +- **variable** - Variable to manage. e.g. `net.ipv4.ip_forward`, `vm.swappiness`. +- **value** - Value to affect to variable. e.g. `1`, `0`. +- **path** - Path to a specific file. + +### Examples + +###ruby + + # Set 'vm.swappiness' to '60'. Will create /etc/sysctl.d/40-vm.wappiness.conf + sysctl 'vm.swappiness' do + value '40' + end + +####the same. will create `/etc/sysctl.d/40-vm_swappiness_to_60.conf` + + sysctl 'vm swappiness to 60' do + variable 'vm.swappiness' + value '60' + end + +####Remove /etc/sysctl.d/40-ip_config.conf + sysctl 'ip config' do + action :remove + end + +#### Set swappiness but don't save it. + sysctl 'vm.swappiness' do + action :set + value '40' + end + + +sysctl_multi +------------ + +### Actions + +- **:save** - Save and set a sysctl value (default). +- **:set** - set a sysctl value. +- **:remove** - remove a (previous set) sysctl. + +### Attribute Parameters + +- **instructions** - Hash with instruction. e.g. `{variable => value, variable2 => value2}`. + Override use of 'variable' and 'value'. +- **path** - Path to a specific file. + +### Examples + +####ruby +### set multi variables. will create /etc/sysctl.d/69-ip_config.conf + sysctl_multi 'ip config' do + instructions { + 'net.ipv4.ip_forward' => '1', + 'net.ipv6.conf.all.forwarding' => '1', + 'net.ipv4.tcp_syncookies' => '1'} + end + diff --git a/chef/cookbooks/sysctl/metadata.rb b/chef/cookbooks/sysctl/metadata.rb new file mode 100644 index 0000000..724dfb5 --- /dev/null +++ b/chef/cookbooks/sysctl/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Rackspace US, Inc." +name "sysctl" +license "Apache v2.0" +description "Configures sysctl parameters" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.1.15" +%w{ debian ubuntu centos redhat }.each do |os| + supports os +end diff --git a/chef/cookbooks/sysctl/mysql/CHANGELOG.md b/chef/cookbooks/sysctl/mysql/CHANGELOG.md new file mode 100644 index 0000000..c1c710f --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/CHANGELOG.md @@ -0,0 +1,53 @@ +## v1.2.6: + +* [COOK-1113] - Use an attribute to determine if upstart is used +* [COOK-1121] - Add support for Windows +* [COOK-1140] - Fix conf.d on Debian +* [COOK-1151] - Fix server_ec2 handling /var/lib/mysql bind mount +* [COOK-1321] - Document setting password attributes for solo + +## v1.2.4 + +* [COOK-992] - fix FATAL nameerror +* [COOK-827] - `mysql:server_ec2` recipe can't mount `data_dir` +* [COOK-945] - FreeBSD support + +## v1.2.2 + +* [COOK-826] mysql::server recipe doesn't quote password string +* [COOK-834] Add 'scientific' and 'amazon' platforms to mysql cookbook + +## v1.2.1 + +* [COOK-644] Mysql client cookbook 'package missing' error message is confusing +* [COOK-645] RHEL6/CentOS6 - mysql cookbook contains 'skip-federated' directive which is unsupported on MySQL 5.1 + +## v1.2.0 + +* [COOK-684] remove mysql_database LWRP + +## v1.0.8: + +* [COOK-633] ensure "cloud" attribute is available + +## v1.0.7: + +* [COOK-614] expose all mysql tunable settings in config +* [COOK-617] bind to private IP if available + +## v1.0.6: + +* [COOK-605] install mysql-client package on ubuntu/debian + +## v1.0.5: + +* [COOK-465] allow optional remote root connections to mysql +* [COOK-455] improve platform version handling +* externalize conf_dir attribute for easier cross platform support +* change datadir attribute to data_dir for consistency + +## v1.0.4: + +* fix regressions on debian platform +* [COOK-578] wrap root password in quotes +* [COOK-562] expose all tunables in my.cnf diff --git a/chef/cookbooks/sysctl/mysql/CONTRIBUTING b/chef/cookbooks/sysctl/mysql/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/sysctl/mysql/Gemfile b/chef/cookbooks/sysctl/mysql/Gemfile new file mode 100644 index 0000000..71fb2b7 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/Gemfile @@ -0,0 +1,8 @@ +source :rubygems + +gem 'cucumber', '~> 1.1.8' +gem 'minitest', '~> 3.0.0' + +group :kitchen do + gem 'test-kitchen', :git => 'git@github.com:opscode/test-kitchen.git' +end diff --git a/chef/cookbooks/sysctl/mysql/LICENSE b/chef/cookbooks/sysctl/mysql/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/sysctl/mysql/README.md b/chef/cookbooks/sysctl/mysql/README.md new file mode 100644 index 0000000..db4ae91 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/README.md @@ -0,0 +1,125 @@ +Description +=========== + +Installs and configures MySQL client or server. + +Requirements +============ + +Platform +-------- + +* Debian, Ubuntu +* CentOS, Red Hat, Fedora +* Mac OS X (Using homebrew) + +Tested on: + +* Debian 5.0 +* Ubuntu 10.04 +* CentOS 5.5 +* Mac OS X 10.7.2 + +Cookbooks +--------- + +Requires Opscode's openssl cookbook for secure password generation. See _Attributes_ and _Usage_ for more information. + +Requires a C compiler and Ruby development package in order to build mysql gem with native extensions. On Debian and Ubuntu systems this is satisfied by installing the "build-essential" and "ruby-dev" packages before running Chef. See USAGE below for information on how to handle this during a Chef run. + +Requires homebrew cookbook on Mac OS X. + +Resources and Providers +======================= + +The LWRP that used to ship as part of this cookbook has been refactored into the [database](https://github.com/opscode/cookbooks/tree/master/database) cookbook. Please see the README for details on updated usage. + +Attributes +========== + +* `mysql['server_root_password']` - Set the server's root password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql['server_repl_password']` - Set the replication user 'repl' password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql['server_debian_password']` - Set the debian-sys-maint user password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql['bind_address']` - Listen address for MySQLd, default is node's ipaddress. +* `mysql['data_dir']` - Location for mysql data directory, default is "/var/lib/mysql" +* `mysql['conf_dir']` - Location for mysql conf directory, default is "/etc/mysql" +* `mysql['ec2_path']` - location of mysql data_dir on EC2 nodes, default "/mnt/mysql" +* `mysql['reload_action']` - Action to take when mysql conf files are modified, default "restart". Also allows "reload" and "none". + + +Performance tuning attributes, each corresponds to the same-named parameter in my.cnf; default values listed + +* `mysql['tunable']['key_buffer']` = "250M" +* `mysql['tunable']['max_connections']` = "800" +* `mysql['tunable']['wait_timeout']` = "180" +* `mysql['tunable']['net_write_timeout']` = "30" +* `mysql['tunable']['net_write_timeout']` = "30" +* `mysql['tunable']['back_log']` = "128" +* `mysql['tunable']['table_cache']` = "128" +* `mysql['tunable']['max_heap_table_size']` = "32M" +* `mysql['tunable']['expire_logs_days']` = "10" +* `mysql['tunable']['max_binlog_size']` = "100M" + +Usage +===== + +On client nodes, use the client (or default) recipe: + + include_recipe "mysql::client" + +This will install the MySQL client libraries and development headers on the system. It will also install the Ruby Gem `mysql`, so that the cookbook's LWRP (above) can be used. This is done during the compile-phase of the Chef run. On platforms that are known to have a native package (currently Debian, Ubuntu, Red hat, Centos, Fedora and SUSE), the package will be installed. Other platforms will use the RubyGem. + +This creates a resource object for the package and does the installation before other recipes are parsed. You'll need to have the C compiler and such (ie, build-essential on Ubuntu) before running the recipes, but we already do that when installing Chef :-). + +On server nodes, use the server recipe: + + include_recipe "mysql::server" + +On Debian and Ubuntu, this will preseed the mysql-server package with the randomly generated root password in the recipe file. On other platforms, it simply installs the required packages. It will also create an SQL file, /etc/mysql/grants.sql, that will be used to set up grants for the root, repl and debian-sys-maint users. + +The recipe will perform a `node.save` unless it is run under `chef-solo` after the password attributes are used to ensure that in the event of a failed run, the saved attributes would be used. + +**Chef Solo Note**: These node attributes are stored on the Chef server when using `chef-client`. Because `chef-solo` does not connect to a server or save the node object at all, to have the same passwords persist across `chef-solo` runs, you must specify them in the `json_attribs` file used. For example: + + { + "mysql": { + "server_root_password": "iloverandompasswordsbutthiswilldo", + "server_repl_password": "iloverandompasswordsbutthiswilldo", + "server_debian_password": "iloverandompasswordsbutthiswilldo" + }, + "run_list":["recipe[mysql::server]"] + } + +On EC2 nodes, use the `server_ec2` recipe and the mysql data dir will be set up in the ephmeral storage. + + include_recipe "mysql::server_ec2" + +When the `ec2_path` doesn't exist we look for a mounted filesystem (eg, EBS) and move the data_dir there. + +The client recipe is already included by server and 'default' recipes. + +For more infromation on the compile vs execution phase of a Chef run: + +* http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run + +License and Author +================== + +Author:: Joshua Timberman () +Author:: AJ Christensen () +Author:: Seth Chisamore () +Author:: Brian Bianco () + +Copyright:: 2009-2011 Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/sysctl/mysql/attributes/client.rb b/chef/cookbooks/sysctl/mysql/attributes/client.rb new file mode 100644 index 0000000..a6d2ec0 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/attributes/client.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: mysql +# Attributes:: server +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node["platform"] +when "windows" + default['mysql']['client']['version'] = "6.0.2" + default['mysql']['client']['arch'] = "win32" # force 32 bit to work with mysql gem + default['mysql']['client']['package_file'] = "mysql-connector-c-#{mysql['client']['version']}-#{mysql['client']['arch']}.msi" + default['mysql']['client']['url'] = "http://www.mysql.com/get/Downloads/Connector-C/#{mysql['client']['package_file']}/from/http://mysql.mirrors.pair.com/" + default['mysql']['client']['package_name'] = "MySQL Connector C #{mysql['client']['version']}" + + default['mysql']['client']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\Program Files (x86)\\MySQL\\#{mysql['client']['package_name']}" + default['mysql']['client']['lib_dir'] = "#{mysql['client']['basedir']}\\lib/opt" + default['mysql']['client']['bin_dir'] = "#{mysql['client']['basedir']}\\bin" + default['mysql']['client']['ruby_dir'] = RbConfig::CONFIG['bindir'] +end diff --git a/chef/cookbooks/sysctl/mysql/attributes/server.rb b/chef/cookbooks/sysctl/mysql/attributes/server.rb new file mode 100644 index 0000000..0bf9903 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/attributes/server.rb @@ -0,0 +1,157 @@ +# +# Cookbook Name:: mysql +# Attributes:: server +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default["susan"]=0 +default["susan2"]=0 + +default['mysql']['bind_address'] = attribute?('cloud') ? cloud['local_ipv4'] : node["network"]["interfaces"]["eth1"]["addresses"].keys[1] +default['mysql']['port'] = 3306 + +case node["platform"] +when "centos", "redhat", "fedora", "suse", "scientific", "amazon" + default['mysql']['package_name'] = "mysql-server" + default['mysql']['service_name'] = "mysqld" + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + + set['mysql']['conf_dir'] = '/etc' + set['mysql']['confd_dir'] = '/etc/mysql/conf.d' + set['mysql']['socket'] = "/var/lib/mysql/mysql.sock" + set['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + set['mysql']['old_passwords'] = 1 + set['mysql']['grants_path'] = "/etc/mysql_grants.sql" + # RHEL/CentOS mysql package does not support this option. + set['mysql']['tunable']['innodb_adaptive_flushing'] = false +when "freebsd" + default['mysql']['package_name'] = "mysql55-server" + default['mysql']['service_name'] = "mysql-server" + default['mysql']['basedir'] = "/usr/local" + default['mysql']['data_dir'] = "/var/db/mysql" + default['mysql']['root_group'] = "wheel" + default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" + + set['mysql']['conf_dir'] = '/usr/local/etc' + set['mysql']['confd_dir'] = '/usr/local/etc/mysql/conf.d' + set['mysql']['socket'] = "/tmp/mysqld.sock" + set['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + set['mysql']['old_passwords'] = 0 + set['mysql']['grants_path'] = "/var/db/mysql/grants.sql" +when "windows" + default['mysql']['package_name'] = "MySQL Server 5.5" + default['mysql']['version'] = '5.5.21' + default['mysql']['arch'] = 'win32' + default['mysql']['package_file'] = "mysql-#{mysql['version']}-#{mysql['arch']}.msi" + default['mysql']['url'] = "http://www.mysql.com/get/Downloads/MySQL-5.5/#{mysql['package_file']}/from/http://mysql.mirrors.pair.com/" + + default['mysql']['service_name'] = "mysql" + default['mysql']['basedir'] = "#{ENV['SYSTEMDRIVE']}\\Program Files (x86)\\MySQL\\#{mysql['package_name']}" + default['mysql']['data_dir'] = "#{mysql['basedir']}\\Data" + default['mysql']['bin_dir'] = "#{mysql['basedir']}\\bin" + default['mysql']['mysqladmin_bin'] = "#{mysql['bin_dir']}\\mysqladmin" + default['mysql']['mysql_bin'] = "#{mysql['bin_dir']}\\mysql" + + default['mysql']['conf_dir'] = "#{mysql['basedir']}" + default['mysql']['old_passwords'] = 0 + default['mysql']['grants_path'] = "#{mysql['conf_dir']}\\grants.sql" +when "mac_os_x" + default['mysql']['package_name'] = "mysql" + default['mysql']['basedir'] = "/usr/local/Cellar" + default['mysql']['data_dir'] = "/usr/local/var/mysql" + default['mysql']['root_group'] = "admin" + default['mysql']['mysqladmin_bin'] = "/usr/local/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/local/bin/mysql" +else + default['mysql']['package_name'] = "mysql-server" + default['mysql']['service_name'] = "mysql" + default['mysql']['basedir'] = "/usr" + default['mysql']['data_dir'] = "/var/lib/mysql" + default['mysql']['root_group'] = "root" + default['mysql']['mysqladmin_bin'] = "/usr/bin/mysqladmin" + default['mysql']['mysql_bin'] = "/usr/bin/mysql" + + set['mysql']['conf_dir'] = '/etc/mysql' + set['mysql']['confd_dir'] = '/etc/mysql/conf.d' + set['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" + set['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + set['mysql']['old_passwords'] = 0 + set['mysql']['grants_path'] = "/etc/mysql/grants.sql" +end + +if attribute?('ec2') + default['mysql']['ec2_path'] = "/mnt/mysql" + default['mysql']['ebs_vol_dev'] = "/dev/sdi" + default['mysql']['ebs_vol_size'] = 50 +end + +default['mysql']['reload_action'] = "restart" # or "reload" or "none" + +default['mysql']['use_upstart'] = platform?("ubuntu") && node.platform_version.to_f >= 10.04 + +default['mysql']['auto-increment-increment'] = 1 +default['mysql']['auto-increment-offset'] = 1 + +default['mysql']['allow_remote_root'] = false +default['mysql']['tunable']['back_log'] = "128" +default['mysql']['tunable']['key_buffer'] = "256M" +default['mysql']['tunable']['max_allowed_packet'] = "16M" +default['mysql']['tunable']['max_connections'] = "800" +default['mysql']['tunable']['max_heap_table_size'] = "32M" +default['mysql']['tunable']['myisam_recover'] = "BACKUP" +default['mysql']['tunable']['net_read_timeout'] = "30" +default['mysql']['tunable']['net_write_timeout'] = "30" +default['mysql']['tunable']['table_cache'] = "128" +default['mysql']['tunable']['table_open_cache'] = "128" +default['mysql']['tunable']['thread_cache'] = "128" +default['mysql']['tunable']['thread_cache_size'] = 8 +default['mysql']['tunable']['thread_concurrency'] = 10 +default['mysql']['tunable']['thread_stack'] = "256K" +default['mysql']['tunable']['wait_timeout'] = "180" + +default['mysql']['tunable']['log_bin'] = nil +default['mysql']['tunable']['log_bin_trust_function_creators'] = false +default['mysql']['tunable']['relay_log'] = nil +default['mysql']['tunable']['log_slave_updates'] = false +default['mysql']['tunable']['sync_binlog'] = 0 +default['mysql']['tunable']['skip_slave_start'] = false + +default['mysql']['tunable']['log_error'] = nil +default['mysql']['tunable']['log_queries_not_using_index'] = true +default['mysql']['tunable']['log_bin_trust_function_creators'] = false + +default['mysql']['tunable']['innodb_buffer_pool_size'] = "128M" +default['mysql']['tunable']['innodb_log_file_size'] = "5M" +default['mysql']['tunable']['innodb_buffer_pool_size'] = "128M" +default['mysql']['tunable']['innodb_additional_mem_pool_size'] = "8M" +default['mysql']['tunable']['innodb_data_file_path'] = "ibdata1:10M:autoextend" +default['mysql']['tunable']['innodb_flush_log_at_trx_commit'] = "1" +default['mysql']['tunable']['innodb_flush_method'] = false +default['mysql']['tunable']['innodb_log_buffer_size'] = "8M" + +default['mysql']['tunable']['query_cache_limit'] = "1M" +default['mysql']['tunable']['query_cache_size'] = "16M" + +default['mysql']['tunable']['log_slow_queries'] = "/var/log/mysql/slow.log" +default['mysql']['tunable']['long_query_time'] = 2 + +default['mysql']['tunable']['expire_logs_days'] = 10 +default['mysql']['tunable']['max_binlog_size'] = "100M" diff --git a/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/helpers.rb b/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/helpers.rb new file mode 100644 index 0000000..c8b3fa2 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/helpers.rb @@ -0,0 +1,11 @@ +module Helpers + module Mysql + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + def assert_secure_password(type) + node["mysql"]["server_#{type}_password"].length.must_be_close_to(20, 8) + end + end +end diff --git a/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/server_test.rb b/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/server_test.rb new file mode 100644 index 0000000..f9341aa --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/files/default/tests/minitest/server_test.rb @@ -0,0 +1,34 @@ +require File.expand_path('../helpers', __FILE__) + +describe 'mysql::server' do + + include Helpers::Mysql + + it 'has a secure operating system password' do + assert_secure_password(:debian) + end + it 'has a secure root password' do + assert_secure_password(:root) + end + it 'has a secure replication password' do + assert_secure_password(:repl) + end + it 'installs the mysql package' do + package(node['mysql']['package_name']).must_be_installed + end + it 'has a config directory' do + directory(node['mysql']['confd_dir']).must_exist.with(:owner, 'mysql').and(:group, 'mysql') + end + it 'runs as a daemon' do + service(node['mysql']['service_name']).must_be_running + end + it 'creates a my.cnf' do + file("#{node['mysql']['conf_dir']}/my.cnf").must_exist + end + describe 'debian' do + it 'creates a config file for service control' do + skip unless ['debian', 'ubuntu'].include?(node[:platform]) + file("#{node['mysql']['conf_dir']}/debian.cnf").must_exist + end + end +end diff --git a/chef/cookbooks/sysctl/mysql/libraries/helpers.rb b/chef/cookbooks/sysctl/mysql/libraries/helpers.rb new file mode 100644 index 0000000..ec8510e --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/libraries/helpers.rb @@ -0,0 +1,33 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Opscode + module Mysql + module Helpers + + def debian_before_squeeze? + platform?("debian") && (node.platform_version.to_f < 6.0) + end + + def ubuntu_before_lucid? + platform?("ubuntu") && (node.platform_version.to_f < 10.0) + end + + end + end +end \ No newline at end of file diff --git a/chef/cookbooks/sysctl/mysql/metadata.rb b/chef/cookbooks/sysctl/mysql/metadata.rb new file mode 100644 index 0000000..dbee13d --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/metadata.rb @@ -0,0 +1,138 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures mysql for client or server" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.2.7" +recipe "mysql", "Includes the client recipe to configure a client" +recipe "mysql::client", "Installs packages required for mysql clients using run_action magic" +recipe "mysql::server", "Installs packages required for mysql servers w/o manual intervention" +recipe "mysql::server_ec2", "Performs EC2-specific mountpoint manipulation" + +%w{ debian ubuntu centos suse fedora redhat scientific amazon freebsd windows mac_os_x }.each do |os| + supports os +end + +depends "openssl" +suggests "homebrew" +suggests "windows" + +attribute "mysql/server_root_password", + :display_name => "MySQL Server Root Password", + :description => "Randomly generated password for the mysqld root user", + :default => "randomly generated" + +attribute "mysql/bind_address", + :display_name => "MySQL Bind Address", + :description => "Address that mysqld should listen on", + :default => "ipaddress" + +attribute "mysql/data_dir", + :display_name => "MySQL Data Directory", + :description => "Location of mysql databases", + :default => "/var/lib/mysql" + +attribute "mysql/conf_dir", + :display_name => "MySQL Conf Directory", + :description => "Location of mysql conf files", + :default => "/etc/mysql" + +attribute "mysql/ec2_path", + :display_name => "MySQL EC2 Path", + :description => "Location of mysql directory on EC2 instance EBS volumes", + :default => "/mnt/mysql" + +attribute "mysql/reload_action", + :display_name => "MySQL conf file reload action", + :description => "Action to take when mysql conf files are modified", + :default => "reload" + +attribute "mysql/tunable", + :display_name => "MySQL Tunables", + :description => "Hash of MySQL tunable attributes", + :type => "hash" + +attribute "mysql/tunable/key_buffer", + :display_name => "MySQL Tuntable Key Buffer", + :default => "250M" + +attribute "mysql/tunable/max_connections", + :display_name => "MySQL Tunable Max Connections", + :default => "800" + +attribute "mysql/tunable/wait_timeout", + :display_name => "MySQL Tunable Wait Timeout", + :default => "180" + +attribute "mysql/tunable/net_read_timeout", + :display_name => "MySQL Tunable Net Read Timeout", + :default => "30" + +attribute "mysql/tunable/net_write_timeout", + :display_name => "MySQL Tunable Net Write Timeout", + :default => "30" + +attribute "mysql/tunable/back_log", + :display_name => "MySQL Tunable Back Log", + :default => "128" + +attribute "mysql/tunable/table_cache", + :display_name => "MySQL Tunable Table Cache for MySQL < 5.1.3", + :default => "128" + +attribute "mysql/tunable/table_open_cache", + :display_name => "MySQL Tunable Table Cache for MySQL >= 5.1.3", + :default => "128" + +attribute "mysql/tunable/max_heap_table_size", + :display_name => "MySQL Tunable Max Heap Table Size", + :default => "32M" + +attribute "mysql/tunable/expire_logs_days", + :display_name => "MySQL Exipre Log Days", + :default => "10" + +attribute "mysql/tunable/max_binlog_size", + :display_name => "MySQL Max Binlog Size", + :default => "100M" + +attribute "mysql/client", + :display_name => "MySQL Connector/C Client", + :description => "Hash of MySQL client attributes", + :type => "hash" + +attribute "mysql/client/version", + :display_name => "MySQL Connector/C Version", + :default => "6.0.2" + +attribute "mysql/client/arch", + :display_name => "MySQL Connector/C Architecture", + :default => "win32" + +attribute "mysql/client/package_file", + :display_name => "MySQL Connector/C Package File Name", + :default => "mysql-connector-c-6.0.2-win32.msi" + +attribute "mysql/client/url", + :display_name => "MySQL Connector/C Download URL", + :default => "http://www.mysql.com/get/Downloads/Connector-C/mysql-connector-c-6.0.2-win32.msi/from/http://mysql.mirrors.pair.com/" + +attribute "mysql/client/package_name", + :display_name => "MySQL Connector/C Registry DisplayName", + :default => "MySQL Connector C 6.0.2" + +attribute "mysql/client/basedir", + :display_name => "MySQL Connector/C Base Install Directory", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2" + +attribute "mysql/client/lib_dir", + :display_name => "MySQL Connector/C Library Directory (containing libmysql.dll)", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\lib\\opt" + +attribute "mysql/client/bin_dir", + :display_name => "MySQL Connector/C Executable Directory", + :default => "C:\\Program Files (x86)\\MySQL\\Connector C 6.0.2\\bin" + +attribute "mysql/client/ruby_dir", + :display_name => "Ruby Executable Directory which should gain MySQL support", + :default => "system ruby" diff --git a/chef/cookbooks/sysctl/mysql/recipes/client.rb b/chef/cookbooks/sysctl/mysql/recipes/client.rb new file mode 100644 index 0000000..7ec75d0 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/recipes/client.rb @@ -0,0 +1,83 @@ +# +# Cookbook Name:: mysql +# Recipe:: client +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Include Opscode helper in Recipe class to get access +# to debian_before_squeeze? and ubuntu_before_lucid? +::Chef::Recipe.send(:include, Opscode::Mysql::Helpers) + +mysql_packages = case node['platform'] +when "centos", "redhat", "suse", "fedora", "scientific", "amazon" + %w{mysql mysql-devel} +when "ubuntu","debian" + if debian_before_squeeze? || ubuntu_before_lucid? + %w{mysql-client libmysqlclient15-dev} + else + %w{mysql-client libmysqlclient-dev} + end +when "freebsd" + %w{mysql55-client} +when "windows" + package_file = node['mysql']['client']['package_file'] + remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do + source node['mysql']['client']['url'] + not_if { File.exists? "#{Chef::Config[:file_cache_path]}/#{package_file}" } + end + + windows_package node['mysql']['client']['package_name'] do + source "#{Chef::Config[:file_cache_path]}/#{package_file}" + end + windows_path node['mysql']['client']['bin_dir'] do + action :add + end + def package(*args, &blk) + windows_package(*args, &blk) + end + [node['mysql']['client']['package_name']] +when "mac_os_x" + include_recipe 'homebrew' + %w{mysql-connector-c} +else + %w{mysql-client libmysqlclient-dev} +end + +mysql_packages.each do |mysql_pack| + package mysql_pack do + action :install + end +end + +if platform?(%w{ redhat centos fedora suse scientific amazon }) + package 'ruby-mysql' +elsif platform?(%w{ debian ubuntu }) + package "libmysql-ruby" +else + gem_package "mysql" do + action :install + end +end + +if platform? 'windows' + ruby_block "copy libmysql.dll into ruby path" do + block do + require 'fileutils' + FileUtils.cp "#{node['mysql']['client']['lib_dir']}\\libmysql.dll", node['mysql']['client']['ruby_dir'] + end + not_if { File.exist?("#{node['mysql']['client']['ruby_dir']}\\libmysql.dll") } + end +end diff --git a/chef/cookbooks/sysctl/mysql/recipes/default.rb b/chef/cookbooks/sysctl/mysql/recipes/default.rb new file mode 100644 index 0000000..9ff90d6 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "mysql::client" diff --git a/chef/cookbooks/sysctl/mysql/recipes/server.rb b/chef/cookbooks/sysctl/mysql/recipes/server.rb new file mode 100644 index 0000000..f7343fd --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/recipes/server.rb @@ -0,0 +1,199 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) + +include_recipe "mysql::client" + +# generate all passwords +node.set_unless['mysql']['server_debian_password'] = secure_password +node.set_unless['mysql']['server_root_password'] = secure_password +node.set_unless['mysql']['server_repl_password'] = secure_password + +if platform?(%w{debian ubuntu}) + + directory "/var/cache/local/preseeding" do + owner "root" + group node['mysql']['root_group'] + mode 0755 + recursive true + end + + execute "preseed mysql-server" do + command "debconf-set-selections /var/cache/local/preseeding/mysql-server.seed" + action :nothing + end + + template "/var/cache/local/preseeding/mysql-server.seed" do + source "mysql-server.seed.erb" + owner "root" + group node['mysql']['root_group'] + mode "0600" + notifies :run, resources(:execute => "preseed mysql-server"), :immediately + end + + template "#{node['mysql']['conf_dir']}/debian.cnf" do + source "debian.cnf.erb" + owner "root" + group node['mysql']['root_group'] + mode "0600" + end + +end + +if platform? 'windows' + package_file = node['mysql']['package_file'] + + remote_file "#{Chef::Config[:file_cache_path]}/#{package_file}" do + source node['mysql']['url'] + not_if { File.exists? "#{Chef::Config[:file_cache_path]}/#{package_file}" } + end + + windows_package node['mysql']['package_name'] do + source "#{Chef::Config[:file_cache_path]}/#{package_file}" + end + + def package(*args, &blk) + windows_package(*args, &blk) + end +end + +package node['mysql']['package_name'] do + action :install +end + +unless platform?(%w{mac_os_x}) + + directory node['mysql']['confd_dir'] do + owner "mysql" unless platform? 'windows' + group "mysql" unless platform? 'windows' + action :create + recursive true + end + + if platform? 'windows' + require 'win32/service' + + windows_path node['mysql']['bin_dir'] do + action :add + end + + windows_batch "install mysql service" do + command "\"#{node['mysql']['bin_dir']}\\mysqld.exe\" --install #{node['mysql']['service_name']}" + not_if { Win32::Service.exists?(node['mysql']['service_name']) } + end + end + + service "mysql" do + service_name node['mysql']['service_name'] + if node['mysql']['use_upstart'] + restart_command "restart mysql" + stop_command "stop mysql" + start_command "start mysql" + end + supports :status => true, :restart => true, :reload => true + action :nothing + end + + skip_federated = case node['platform'] + when 'fedora', 'ubuntu', 'amazon' + true + when 'centos', 'redhat', 'scientific' + node['platform_version'].to_f < 6.0 + else + false + end + + template "#{node['mysql']['conf_dir']}/my.cnf" do + source "my.cnf.erb" + owner "root" unless platform? 'windows' + group node['mysql']['root_group'] unless platform? 'windows' + mode "0644" + case node['mysql']['reload_action'] + when 'restart' + notifies :restart, resources(:service => "mysql"), :immediately + when 'reload' + notifies :reload, resources(:service => "mysql"), :immediately + else + Chef::Log.info "my.cnf updated but mysql.reload_action is #{node['mysql']['reload_action']}. No action taken." + end + variables :skip_federated => skip_federated + end +end + +unless Chef::Config[:solo] + ruby_block "save node data" do + block do + node.save + end + action :create + end +end + +# set the root password on platforms +# that don't support pre-seeding +unless platform?(%w{debian ubuntu}) + + execute "assign-root-password" do + command "\"#{node['mysql']['mysqladmin_bin']}\" -u root password \"#{node['mysql']['server_root_password']}\"" + action :run + only_if "\"#{node['mysql']['mysql_bin']}\" -u root -e 'show databases;'" + end + +end + +# Homebrew has its own way to do databases +if platform?(%w{mac_os_x}) + + execute "mysql-install-db" do + command "mysql_install_db --verbose --user=`whoami` --basedir=\"$(brew --prefix mysql)\" --datadir=#{node['mysql']['data_dir']} --tmpdir=/tmp" + environment('TMPDIR' => nil) + action :run + creates "#{node['mysql']['data_dir']}/mysql" + end + +else + grants_path = node['mysql']['grants_path'] + begin + t = resources("template[#{grants_path}]") + rescue + Chef::Log.info("Could not find previously defined grants.sql resource") + t = template grants_path do + source "grants.sql.erb" + owner "root" unless platform? 'windows' + group node['mysql']['root_group'] unless platform? 'windows' + mode "0600" + action :create + end + end + + if platform? 'windows' + windows_batch "mysql-install-privileges" do + command "\"#{node['mysql']['mysql_bin']}\" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }\"#{node['mysql']['server_root_password']}\" < \"#{grants_path}\"" + action :nothing + subscribes :run, resources("template[#{grants_path}]"), :immediately + end + else + execute "mysql-install-privileges" do + command "\"#{node['mysql']['mysql_bin']}\" -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }\"#{node['mysql']['server_root_password']}\" < \"#{grants_path}\"" + action :nothing + subscribes :run, resources("template[#{grants_path}]"), :immediately + end + end +end diff --git a/chef/cookbooks/sysctl/mysql/recipes/server_ec2.rb b/chef/cookbooks/sysctl/mysql/recipes/server_ec2.rb new file mode 100644 index 0000000..6033ef4 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/recipes/server_ec2.rb @@ -0,0 +1,51 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +if (node.attribute?('ec2') && ! FileTest.directory?(node['mysql']['ec2_path'])) + + service "mysql" do + action :stop + end + + execute "install-mysql" do + command "mv #{node['mysql']['data_dir']} #{node['mysql']['ec2_path']}" + not_if do FileTest.directory?(node['mysql']['ec2_path']) end + end + + [node['mysql']['ec2_path'], node['mysql']['data_dir']].each do |dir| + directory dir do + owner "mysql" + group "mysql" + end + end + + mount node['mysql']['data_dir'] do + device node['mysql']['ec2_path'] + fstype "none" + options "bind,rw" + action [:mount, :enable] + end + + service "mysql" do + action :start + end + +end + diff --git a/chef/cookbooks/sysctl/mysql/templates/default/debian.cnf.erb b/chef/cookbooks/sysctl/mysql/templates/default/debian.cnf.erb new file mode 100644 index 0000000..989b125 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/default/debian.cnf.erb @@ -0,0 +1,12 @@ +[client] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> + +[mysql_upgrade] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> +basedir = /usr diff --git a/chef/cookbooks/sysctl/mysql/templates/default/grants.sql.erb b/chef/cookbooks/sysctl/mysql/templates/default/grants.sql.erb new file mode 100644 index 0000000..bfb77c6 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/default/grants.sql.erb @@ -0,0 +1,15 @@ +# Generated by Chef for <%= node['hostname'] %>. +# Local modifications will be overwritten. + +<% case node['platform'] -%> +<% when "debian","ubuntu" -%> +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '<%= node[:mysql][:server_debian_password] %>' WITH GRANT OPTION; +<% end -%> +# Grant replication for a slave user. +GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' identified by '<%= node['mysql']['server_repl_password'] %>'; + +# Set the server root password. This should be preseeded by the package installation. +<% if node['mysql']['allow_remote_root'] -%> +GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '<%= node['mysql']['server_root_password'] %>' WITH GRANT OPTION; +<% end -%> +SET PASSWORD FOR 'root'@'localhost' = PASSWORD('<%= node['mysql']['server_root_password'] %>'); diff --git a/chef/cookbooks/sysctl/mysql/templates/default/my.cnf.erb b/chef/cookbooks/sysctl/mysql/templates/default/my.cnf.erb new file mode 100644 index 0000000..2ffb007 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/default/my.cnf.erb @@ -0,0 +1,211 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# You can copy this to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +# Remember to edit /etc/mysql/debian.cnf when changing the socket location. +[client] +port = <%= node['mysql']['port'] %> +socket = <%= node['mysql']['socket'] %> + +# Here is entries for some specific programs +# The following values assume you have at least 32M ram + +# This was formally known as [safe_mysqld]. Both versions are currently parsed. +[mysqld_safe] +socket = <%= node['mysql']['socket'] %> +nice = 0 + +[mysqld] +# +# * Basic Settings +# + +# +# * IMPORTANT +# If you make changes to these settings and your system uses apparmor, you may +# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. +# + +user = mysql +pid-file = <%= node['mysql']['pid_file'] %> +socket = <%= node['mysql']['socket'] %> +port = <%= node['mysql']['port'] %> +basedir = <%= node['mysql']['basedir'] %> +datadir = <%= node['mysql']['data_dir'] %> +tmpdir = /tmp +skip-external-locking +# +# Instead of skip-networking the default is now to listen only on +# localhost which is more compatible and is not less secure. +bind-address = <%= node['mysql']['bind_address'] %> +# +# * Fine Tuning +# +key_buffer = <%= node['mysql']['tunable']['key_buffer'] %> +max_allowed_packet = <%= node['mysql']['tunable']['max_allowed_packet'] %> +thread_stack = <%= node['mysql']['tunable']['thread_stack'] %> +thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> + +auto-increment-increment = <%= node['mysql']['auto-increment-increment'] %> +auto-increment-offset = <%= node['mysql']['auto-increment-offset'] %> + +# This replaces the startup script and checks MyISAM tables if needed +# the first time they are touched +myisam-recover = BACKUP +#max_connections = 100 +#table_cache = 64 +#thread_concurrency = 10 +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +wait_timeout = <%= node['mysql']['tunable']['wait_timeout'] %> +net_read_timeout = <%= node['mysql']['tunable']['net_read_timeout'] %> +net_write_timeout = <%= node['mysql']['tunable']['net_write_timeout'] %> +back_log = <%= node['mysql']['tunable']['back_log'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +max_heap_table_size = <%= node['mysql']['tunable']['max_heap_table_size'] %> + +# +# * Query Cache Configuration +# +query_cache_limit = <%= node['mysql']['tunable']['query_cache_limit'] %> +query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> +# +# * Logging and Replication +# +# Both location gets rotated by the cronjob. +# Be aware that this log type is a performance killer. +#log = /var/log/mysql/mysql.log +# +# Error logging goes to syslog. This is a Debian improvement :) +<%- if node['mysql']['tunable']['log_error'] %> +log-error = <%= node['mysql']['tunable']['log_error'] %> +<%- end %> +# +# Here you can see queries with especially long duration +log_slow_queries = <%= node['mysql']['tunable']['log_slow_queries'] %> +long_query_time = <%= node['mysql']['tunable']['long_query_time'] %> +<%- if node['mysql']['tunable']['log_queries_not_using_index'] %> +log-queries-not-using-indexes +<%- end %> +# +# The following can be used as easy to replay backup logs or for replication. +# note: if you are setting up a replication slave, see README.Debian about +# other settings you may need to change. +#server-id = 1 +<%- if node['mysql']['tunable']['log_bin'] %> +log_bin = <%= node['mysql']['tunable']['log_bin'] %> +log_slave_updates = <%= node['mysql']['tunable']['log_slave_updates'] %> +<%- end %> +<%- if node['mysql']['tunable']['log_bin_trust_function_creators'] %> +log_bin_trust_function_creators +<%- end %> +expire_logs_days = <%= node['mysql']['tunable']['expire_logs_days'] %> +max_binlog_size = <%= node['mysql']['tunable']['max_binlog_size'] %> +#binlog_do_db = include_database_name +#binlog_ignore_db = include_database_name +<%- if node['mysql']['tunable']['relay_log'] %> +relay-log = <%= node['mysql']['tunable']['relay_log'] %> +<%- end %> +sync_binlog = <%= node['mysql']['tunable']['sync_binlog'] %> +<%- if node['mysql']['tunable']['skip_slave_start'] %> +skip_slave_start +<%- end %> + +# +# * InnoDB +# +# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +#skip-innodb +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +innodb_log_file_size = <%= node['mysql']['tunable']['innodb_log_file_size'] %> +innodb_additional_mem_pool_size = <%= node['mysql']['tunable']['innodb_additional_mem_pool_size'] %> +innodb_data_file_path = <%= node['mysql']['tunable']['innodb_data_file_path'] %> +innodb_file_per_table +innodb_flush_log_at_trx_commit = <%= node['mysql']['tunable']['innodb_flush_log_at_trx_commit'] %> +<%- if node['mysql']['tunable']['innodb_flush_method'] %> +innodb_flush_method = <%= node['mysql']['tunable']['innodb_flush_method'] %> +<%- end %> +innodb_log_buffer_size = <%= node['mysql']['tunable']['innodb_log_buffer_size'] %> +<%- if node['mysql']['tunable']['innodb_adaptive_flushing'] %> +innodb_adaptive_flushing = <%= node['mysql']['tunable']['innodb_adaptive_flushing'] %> +<%- end %> + +<% if @skip_federated %> +# +# * Federated +# +# The FEDERATED storage engine is disabled since 5.0.67 by default in the .cnf files +# shipped with MySQL distributions (my-huge.cnf, my-medium.cnf, and so forth). +# +skip-federated +<% end %> +# +# * Security Features +# +# Read the manual, too, if you want chroot! +# chroot = /var/lib/mysql/ +# +# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". +# +# ssl-ca=/etc/mysql/cacert.pem +# ssl-cert=/etc/mysql/server-cert.pem +# ssl-key=/etc/mysql/server-key.pem + +[mysqldump] +quick +quote-names +max_allowed_packet = <%= node['mysql']['tunable']['max_allowed_packet'] %> + +[mysql] +#no-auto-rehash # faster start of mysql but no tab completition + +[isamchk] +key_buffer = <%= node['mysql']['tunable']['max_allowed_packet'] %> + +# +# * NDB Cluster +# +# See /usr/share/doc/mysql-server-*/README.Debian for more information. +# +# The following configuration is read by the NDB Data Nodes (ndbd processes) +# not from the NDB Management Nodes (ndb_mgmd processes). +# +# [MYSQL_CLUSTER] +# ndb-connectstring=127.0.0.1 + +<% case node['platform'] -%> +<% when "centos", "redhat", "fedora", "suse", "scientific", "amazon"-%> +# +# * BerkeleyDB +# +# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12. +skip-bdb +# Default to using old password format for compatibility with mysql 3.x +# clients (those using the mysqlclient10 compatibility package). +old_passwords = <%= node['mysql']['old_passwords'] %> + +<% else -%> +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +!includedir <%= node['mysql']['confd_dir'] %>/ +<% end -%> diff --git a/chef/cookbooks/sysctl/mysql/templates/default/mysql-server.seed.erb b/chef/cookbooks/sysctl/mysql/templates/default/mysql-server.seed.erb new file mode 100644 index 0000000..a5a74f0 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/default/mysql-server.seed.erb @@ -0,0 +1,10 @@ +mysql-server-5.0 mysql-server/root_password_again select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server/root_password select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server-5.0/really_downgrade boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat boolean false +mysql-server-5.0 mysql-server-5.0/start_on_boot boolean true +mysql-server-5.0 mysql-server/error_setting_password boolean false +mysql-server-5.0 mysql-server-5.0/nis_warning note +mysql-server-5.0 mysql-server-5.0/postrm_remove_databases boolean false +mysql-server-5.0 mysql-server/password_mismatch boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat_done boolean true diff --git a/chef/cookbooks/sysctl/mysql/templates/default/port_mysql.erb b/chef/cookbooks/sysctl/mysql/templates/default/port_mysql.erb new file mode 100644 index 0000000..55a2ffc --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/default/port_mysql.erb @@ -0,0 +1,3 @@ +# MySQL +-A FWR -p tcp -m tcp --dport 3306 -j ACCEPT +-A FWR -p udp -m udp --dport 3306 -j ACCEPT \ No newline at end of file diff --git a/chef/cookbooks/sysctl/mysql/templates/windows/my.cnf.erb b/chef/cookbooks/sysctl/mysql/templates/windows/my.cnf.erb new file mode 100644 index 0000000..f0550c1 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/templates/windows/my.cnf.erb @@ -0,0 +1,61 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +[client] +port = 3306 + +[mysql] +default-character-set = latin1 + +[mysqld] +# +# * Basic Settings +# +port = 3306 +basedir = <%= node['mysql']['basedir'] %> +datadir = <%= node['mysql']['data_dir'] %> +character-set-server = latin1 +default-storage-engine = INNODB +sql-mode = "STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" + +# +# * Fine Tuning +# +thread_cache_size = <%= node['mysql']['tunable']['thread_cache_size'] %> +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +query_cache_size = <%= node['mysql']['tunable']['query_cache_size'] %> +tmp_table_size = 5M +myisam_max_sort_file_size = 100G +myisam_sort_buffer_size = 8M +key_buffer_size = 8M +read_buffer_size = 64K +read_rnd_buffer_size = 256K +sort_buffer_size = 212K + +# +# * InnoDB +# +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +# +innodb_additional_mem_pool_size = 2M +innodb_flush_log_at_trx_commit = 1 +innodb_log_buffer_size = 1M +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +innodb_log_file_size = 10M +innodb_thread_concurrency = 8 diff --git a/chef/cookbooks/sysctl/mysql/test/features/query_database.feature b/chef/cookbooks/sysctl/mysql/test/features/query_database.feature new file mode 100644 index 0000000..3f630d8 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/features/query_database.feature @@ -0,0 +1,26 @@ +@server +Feature: Query database + +In order to persist and retrieve my application data +As a developer +I want to be able to query the database + + Scenario: Query database + Given a new database server with some example data + When I query the database + Then the expected data should be returned + + Scenario: Update a row + Given a new database server with some example data + When I update a row in a database table + Then the updated data should be returned for subsequent queries + + Scenario: Insert a row + Given a new database server with some example data + When I insert a new row into a database table + Then the inserted data should be returned for subsequent queries + + Scenario: Delete a row + Given a new database server with some example data + When I delete a row from a database table + Then the deleted data should not be returned for subsequent queries diff --git a/chef/cookbooks/sysctl/mysql/test/features/step_definitions/mysql_steps.rb b/chef/cookbooks/sysctl/mysql/test/features/step_definitions/mysql_steps.rb new file mode 100644 index 0000000..4b13826 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/features/step_definitions/mysql_steps.rb @@ -0,0 +1,47 @@ +Given 'a new database server with some example data' do + +end + +When 'I delete a row from a database table' do + delete_chef 'Alison Holst' +end + +When 'I query the database' do + select_tv_chefs +end + +When 'I insert a new row into a database table' do + insert_chef 'Ainsley Harriott' +end + +When 'I update a row in a database table' do + update_chef_name('Paula Deen', 'Paula Hiers Deen') +end + +Then 'the expected data should be returned' do + tv_chefs.must_equal(['Alison Holst', 'Nigella Lawson', 'Paula Deen']) +end + +Then 'the inserted data should be returned for subsequent queries' do + begin + select_tv_chefs.must_include 'Ainsley Harriott' + ensure + delete_chef 'Ainsley Harriott' + end +end + +Then 'the deleted data should not be returned for subsequent queries' do + begin + select_tv_chefs.must_equal(['Nigella Lawson', 'Paula Deen']) + ensure + insert_chef 'Alison Holst' + end +end + +Then 'the updated data should be returned for subsequent queries' do + begin + select_tv_chefs.must_equal(['Alison Holst', 'Nigella Lawson', 'Paula Hiers Deen']) + ensure + update_chef_name('Paula Hiers Deen', 'Paula Deen') + end +end diff --git a/chef/cookbooks/sysctl/mysql/test/features/support/env.rb b/chef/cookbooks/sysctl/mysql/test/features/support/env.rb new file mode 100644 index 0000000..8fb0fc0 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/features/support/env.rb @@ -0,0 +1,3 @@ +require 'minitest/spec' +World(MiniTest::Assertions) +MiniTest::Spec.new(nil) diff --git a/chef/cookbooks/sysctl/mysql/test/features/support/mysql_helpers.rb b/chef/cookbooks/sysctl/mysql/test/features/support/mysql_helpers.rb new file mode 100644 index 0000000..8b03fcd --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/features/support/mysql_helpers.rb @@ -0,0 +1,51 @@ +def query(sql) + cmd_prefix = test_client_host ? "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{test_client_host}" : '' + result = %x{echo "#{sql}" | #{cmd_prefix} mysql --host=#{test_server_host} --user=#{test_user} --password=#{test_password} --skip-column-names #{test_database}} + assert $?.success? + result +end +alias :insert :query +alias :update :query +alias :delete :query + +def delete_chef(name) + delete("DELETE FROM tv_chef WHERE name = '#{name}'") +end + +def insert_chef(name) + insert("INSERT INTO tv_chef (name) VALUES('#{name}')") +end + +def select_tv_chefs + @tv_chefs = query('SELECT name FROM tv_chef ORDER BY name').split("\n") + @tv_chefs +end + +def test_client_host + ENV['TEST_CLIENT_HOST'] +end + +def test_database + 'mysql_test' +end + +def test_server_host + ENV['TEST_SERVER_HOST'] || 'localhost' +end + +def test_password + 'neshFiapog' +end + +def test_user + 'test_user' +end + +def tv_chefs + @tv_chefs +end + +def update_chef_name(old_name, new_name) + update("UPDATE tv_chef SET name = '#{new_name}' WHERE name = '#{old_name}'") +end + diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/Kitchenfile b/chef/cookbooks/sysctl/mysql/test/kitchen/Kitchenfile new file mode 100644 index 0000000..8a179b5 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/Kitchenfile @@ -0,0 +1,3 @@ +cookbook "mysql" do + configuration "server" +end diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/README.md b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/README.md new file mode 100644 index 0000000..d710160 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/README.md @@ -0,0 +1,63 @@ +Description +=========== + +This cookbook defines acceptance tests for MySQL. It includes: + +* A `features` sub-directory where the Cucumber features for the database + are defined. + +* Creation of a simple test database for the tests to run against. + +Usage +===== + +Set environment variable `TEST_SERVER_HOST` to specify the MySQL server to +connect to. You can optionally set `TEST_CLIENT_HOST` which will test a client +install by running the same features from a remote client. + +Requirements +============ + +## Cookbooks: + +This cookbook depends on the `mysql` cookbook. It also uses the `database` +cookbook to create the test database and relies on the `yum` cookbook in order +to add the EPEL repository on RHEL-derived distributions. + +## Platforms: + +* Ubuntu +* CentOS + +Attributes +========== + +* `node['mysql_test']['database']` - The name of the test database to create. +* `node['mysql_test']['username']` - The username of the datbase user. +* `node['mysql_test']['password']` - The password of the database user. + +Recipes +======= + +* `client` - Simply includes `mysql::client` for a vanilla mysql client install. +* `server` - Includes `mysql::server` to install the server and configures a + test database. + +License and Authors +=================== + +Author:: Andrew Crump + + Copyright:: 2012, Opscode, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/attributes/default.rb b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/attributes/default.rb new file mode 100644 index 0000000..0826761 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/attributes/default.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: mysql_test +# Attributes:: default +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Must be specified for chef-solo for successful re-converge +override['mysql']['server_root_password'] = 'ebrilvOpCethHienRoj7' + +default['mysql_test']['database'] = 'mysql_test' +default['mysql_test']['username'] = 'test_user' +default['mysql_test']['password'] = 'neshFiapog' + +override['mysql']['bind_address'] = 'localhost' diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/metadata.rb b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/metadata.rb new file mode 100644 index 0000000..7507fe2 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Andrew Crump" +maintainer_email "andrew@kotirisoftware.com" +license "Apache 2.0" +description "Acceptance tests for mysql" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.1.0" + +depends "database" +depends "mysql" +depends "yum" diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/client.rb b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/client.rb new file mode 100644 index 0000000..0af6064 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/client.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: mysql_test +# Recipe:: client +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "yum::epel" if platform?('centos') diff --git a/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/server.rb b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/server.rb new file mode 100644 index 0000000..5c086a5 --- /dev/null +++ b/chef/cookbooks/sysctl/mysql/test/kitchen/cookbooks/mysql_test/recipes/server.rb @@ -0,0 +1,72 @@ +# +# Cookbook Name:: mysql_test +# Recipe:: server +# +# Copyright 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node.platform + when 'ubuntu' + %w{mysql-client libmysqlclient-dev} + when 'centos' + %w{mysql mysql-devel} +end.each do |pkg| + package pkg do + action :nothing + end.run_action(:install) +end + +gem_package "mysql" do + action :nothing +end.run_action(:install) + +include_recipe "yum::epel" if platform?('centos') + +file "/etc/sysconfig/network" do + content "NETWORKING=yes" + action :create_if_missing + only_if { platform?('amazon', 'centos', 'fedora', 'redhat', 'scientific') } +end + +include_recipe 'mysql::server' + +mysql_connection = {:host => "localhost", :username => 'root', + :password => node['mysql']['server_root_password']} + +mysql_database node['mysql_test']['database'] do + connection mysql_connection + action :create +end + +mysql_database_user node['mysql_test']['username'] do + connection mysql_connection + password node['mysql_test']['password'] + database_name node['mysql_test']['database'] + host 'localhost' + privileges [:select,:update,:insert, :delete] + action [:create, :grant] +end + +mysql_conn_args = "--user=root --password=#{node['mysql']['server_root_password']}" + +execute 'create-sample-data' do + command %Q{mysql #{mysql_conn_args} #{node['mysql_test']['database']} < +# +# Copyright 2012, Societe Publica. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +action :save do + + fullname = get_path + + execute "sysctl-p" do + command "sysctl -p #{fullname}" + action :nothing + end + + file get_path do + notifies :run, "execute[sysctl-p]" + content "#{get_variable} = #{new_resource.value}\n" + owner 'root' + group 'root' + mode '0644' + end + new_resource.updated_by_last_action(true) +end + + +action :set do + execute 'set sysctl' do + command "sysctl #{get_variable}=#{new_resource.value}" + end + new_resource.updated_by_last_action(true) +end + + +action :remove do + file get_path do + action :delete + end + new_resource.updated_by_last_action(true) +end + + +def get_path + f_name = new_resource.name.gsub(' ', '_') + priority = new_resource.priority + return new_resource.path ? new_resource.path : \ + "/etc/sysctl.d/#{priority}-#{f_name}.conf" +end + + +def get_variable + return new_resource.variable ? new_resource.variable : new_resource.name +end diff --git a/chef/cookbooks/sysctl/providers/multi.rb b/chef/cookbooks/sysctl/providers/multi.rb new file mode 100644 index 0000000..0728686 --- /dev/null +++ b/chef/cookbooks/sysctl/providers/multi.rb @@ -0,0 +1,68 @@ +# +# Cookbook Name:: sysctl +# Provider:: sysctl +# Author:: Guilhem Lettron +# +# Copyright 20012, Societe Publica. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +action :save do + + fullname = get_path + + execute "sysctl-p" do + command "sysctl -p #{fullname}" + action :nothing + end + + template get_path do + source 'sysctl.conf.erb' + cookbook 'sysctl' + owner 'root' + group 'root' + mode '0644' + variables( + :instructions => new_resource.instructions, + :name => new_resource.name) + notifies :run, "execute[sysctl-p]" + end + new_resource.updated_by_last_action(true) +end + + +action :set do + new_resource.instructions.each do |variable, value| + execute 'set sysctl' do + command "sysctl #{variable}=#{value}" + end + end + new_resource.updated_by_last_action(true) +end + + +action :remove do + file get_path do + action :delete + end + new_resource.updated_by_last_action(true) +end + + +private +def get_path + f_name = new_resource.name.gsub(' ', '_') + priority = new_resource.priority + return new_resource.path ? new_resource.path : \ + "/etc/sysctl.d/#{priority}-#{f_name}.conf" +end diff --git a/chef/cookbooks/sysctl/recipes/default.rb b/chef/cookbooks/sysctl/recipes/default.rb new file mode 100644 index 0000000..98d529e --- /dev/null +++ b/chef/cookbooks/sysctl/recipes/default.rb @@ -0,0 +1,59 @@ +# +# Cookbook Name:: sysctl +# Recipe:: default +# Author:: Guilhem Lettron +# +# Copyright 2012, Societe Publica. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "fake-procps" do + action :install + only_if { platform?("fedora") } +end + + +# TODO(Youscribe) change this by something more "clean". +execute 'remove old files' do + command 'rm --force /etc/sysctl.d/50-chef-attributes-*.conf' + action :run +end + +# redhat supports sysctl.d but doesn't create it by default +directory "/etc/sysctl.d" do + owner 'root' + group 'root' + mode '755' +end + +if node.attribute?('sysctl') + node['sysctl'].each do |item| + f_name = item.first.gsub(' ', '_') + template "/etc/sysctl.d/50-chef-attributes-#{f_name}.conf" do + source 'sysctl.conf.erb' + mode '0644' + owner 'root' + group 'root' + variables(:instructions => item[1]) + notifies :run, "execute[sysctl-p]" + end + end +end + +execute "sysctl-p" do + Dir.glob('/etc/sysctl.d/*.conf').each do |file| + command "sysctl -p #{file}" + end + action :nothing +end diff --git a/chef/cookbooks/sysctl/resources/default.rb b/chef/cookbooks/sysctl/resources/default.rb new file mode 100644 index 0000000..9e23254 --- /dev/null +++ b/chef/cookbooks/sysctl/resources/default.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: sysctl +# Resources:: default +# Author:: Guilhem Lettron +# +# Copyright 2012, Societe Publica. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def initialize(*args) + super + @action = :save +end + +actions :save, :set, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :variable, :kind_of => String, :default => nil +attribute :value, :kind_of => String, :required => true +attribute :path, :kind_of => String, :default => nil +attribute :priority, :kind_of => String, :default => "40" diff --git a/chef/cookbooks/sysctl/resources/multi.rb b/chef/cookbooks/sysctl/resources/multi.rb new file mode 100644 index 0000000..2ac45be --- /dev/null +++ b/chef/cookbooks/sysctl/resources/multi.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: sysctl +# Resources:: multi +# Author:: Guilhem Lettron +# +# Copyright 2012, Societe Publica. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def initialize(*args) + super + @action = :save +end + +actions :save, :set, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :path, :kind_of => String, :default => nil +attribute :instructions, :kind_of => Hash, :default => nil, :required => true +attribute :priority, :kind_of => String, :default => "40" diff --git a/chef/cookbooks/sysctl/templates/default/sysctl.conf.erb b/chef/cookbooks/sysctl/templates/default/sysctl.conf.erb new file mode 100644 index 0000000..6d6d30d --- /dev/null +++ b/chef/cookbooks/sysctl/templates/default/sysctl.conf.erb @@ -0,0 +1,5 @@ +# Sysctl file for <%= @name %> + +<% @instructions.each do |variable, value| -%> +<%= variable %> = <%= value %> +<% end %> \ No newline at end of file diff --git a/chef/cookbooks/windows/CHANGELOG.md b/chef/cookbooks/windows/CHANGELOG.md new file mode 100644 index 0000000..1bd0ee2 --- /dev/null +++ b/chef/cookbooks/windows/CHANGELOG.md @@ -0,0 +1,202 @@ +## Future + +* package preseeding/`response_file` support +* package installation location via a `target_dir` attribute. +* [COOK-666] `windows_package` should support CoApp packages +* WindowsRebootHandler/`windows_reboot` LWRP should support kicking off subsequent chef run on reboot. + +## v1.10.0: + +### Improvement + +- [COOK-3126]: `windows_task` should support the on start frequency +- [COOK-3127]: Support the force option on task create and delete + +## v1.9.0: + +### Bug + +- [COOK-2899]: windows_feature fails when a feature install requires a + reboot +- [COOK-2914]: Foodcritic failures in Cookbooks +- [COOK-2983]: windows cookbook has foodcritic failures + +### Improvement + +- [COOK-2686]: Add Windows Server 2012 to version.rb so other + depending chef scripts can detect Windows Server 2012 + +## v1.8.10: + +When using Windows qualified filepaths (C:/foo), the #absolute? method +for URI returns true, because "C" is the scheme. + +This change checks that the URI is http or https scheme, so it can be +passed off to remote_file appropriately. + +* [COOK-2729] - allow only http, https URI schemes + +## v1.8.8: + +* [COOK-2729] - helper should use URI rather than regex and bare string + +## v1.8.6: + +* [COOK-968] - `windows_package` provider should gracefully handle paths with spaces +* [COOK-222] - `windows_task` resource does not declare :change action +* [COOK-241] - Windows cookbook should check for redefined constants +* [COOK-248] - Windows package install type is case sensitive + +## v1.8.4: + +* [COOK-2336] - MSI That requires reboot returns with RC 3010 and + causes chef run failure +* [COOK-2368] - `version` attribute of the `windows_package` provider + should be documented + +## v1.8.2: + +**Important**: Use powershell in nodes expanded run lists to ensure + powershell is downloaded, as powershell has a dependency on this + cookbook; v1.8.0 created a circular dependency. + +* [COOK-2301] - windows 1.8.0 has circular dependency on powershell + +## v1.8.0: + +* [COOK-2126] - Add checksum attribute to `windows_zipfile` +* [COOK-2142] - Add printer and `printer_port` LWRPs +* [COOK-2149] - Chef::Log.debug Windows Package command line +* [COOK-2155] -`windows_package` does not send checksum to + `cached_file` in `installer_type` + +## v1.7.0: + +* [COOK-1745] - allow for newer versions of rubyzip + +## v1.6.0: + +* [COOK-2048] - undefined method for Falseclass on task :change when + action is :nothing (and task doesn't exist) +* [COOK-2049] - Add `windows_pagefile` resource + +## v1.5.0: + +* [COOK-1251] - Fix LWRP "NotImplementedError" +* [COOK-1921] - Task LWRP will return true for resource exists when no + other scheduled tasks exist +* [COOK-1932] - Include :change functionality to windows task lwrp + +## v1.4.0: + +* [COOK-1571] - `windows_package` resource (with msi provider) does not +accept spaces in filename +* [COOK-1581] - Windows cookbook needs a scheduled tasks LWRP +* [COOK-1584] - `windows_registry` should support all registry types + +## v1.3.4: + +* [COOK-1173] - `windows_registry` throws Win32::Registry::Error for + action :remove on a nonexistent key +* [COOK-1182] - windows package sets start window title instead of + quoting a path +* [COOK-1476] - zipfile lwrp should support :zip action +* [COOK-1485] - package resource fails to perform install correctly + when "source" contains quote +* [COOK-1519] - add action :remove for path lwrp + +## v1.3.2: + +* [COOK-1033] - remove the `libraries/ruby_19_patches.rb` file which + causes havoc on non-Windows systems. +* [COOK-811] - add a timeout parameter attribute for `windows_package` + +## v1.3.0: + +* [COOK-1323] - Update for changes in Chef 0.10.10. + - Setting file mode doesn't make sense on Windows (package provider + - and `reboot_handler` recipe) + - Prefix ::Win32 to avoid namespace collision with Chef::Win32 + - (`registry_helper` library) + - Use chef_gem instead of gem_package so gems get installed correctly + under the Ruby environment Chef runs in (reboot_handler recipe, + zipfile provider) + +## v1.2.12: + +* [COOK-1037] - specify version for rubyzip gem +* [COOK-1007] - `windows_feature` does not work to remove features with + dism +* [COOK-667] - shortcut resource + provider for Windows platforms + +## v1.2.10 + +* [COOK-939] - add `type` parameter to `windows_registry` to allow binary registry keys. +* [COOK-940] - refactor logic so multiple values get created. + +## v1.2.8 + +* FIX: Older Windows (Windows Server 2003) sometimes return 127 on successful forked commands +* FIX: `windows_package`, ensure we pass the WOW* registry redirection flags into reg.open + +## v1.2.6 + +* patch to fix [CHEF-2684], Open4 is named Open3 in Ruby 1.9 +* Ruby 1.9's Open3 returns 0 and 42 for successful commands +* retry keyword can only be used in a rescue block in Ruby 1.9 + +## v1.2.4 + +* `windows_package` - catch Win32::Registry::Error that pops up when searching certain keys + +## v1.2.2 + +* combined numerous helper libarires for easier sharing across libaries/LWRPs +* renamed Chef::Provider::WindowsFeature::Base file to the more descriptive `feature_base.rb` +* refactored `windows_path` LWRP + * :add action should MODIFY the the underlying ENV variable (vs CREATE) + * deleted greedy :remove action until it could be made more idempotent +* added a `windows_batch` resource/provider for running batch scripts remotely + +## v1.2.0 + +* [COOK-745] gracefully handle required server restarts on Windows platform + * WindowsRebootHandler for requested and pending reboots + * `windows_reboot` LWRP for requesting (receiving notifies) reboots + * `reboot_handler` recipe for enabling WindowsRebootHandler as a report handler +* [COOK-714] Correct initialize misspelling +* RegistryHelper - new `get_values` method which returns all values for a particular key. + +## v1.0.8 + +* [COOK-719] resource/provider for managing windows features +* [COOK-717] remove `windows_env_vars` resource as env resource exists in core chef +* new `Windows::Version` helper class +* refactored `Windows::Helper` mixin + +## v1.0.6 + +* added `force_modify` action to `windows_registry` resource +* add `win_friendly_path` helper +* re-purpose default recipe to install useful supporting windows related gems + +## v1.0.4 + +* [COOK-700] new resources and improvements to the `windows_registry` provider (thanks Paul Morton!) + * Open the registry in the bitednes of the OS + * Provide convenience methods to check if keys and values exit + * Provide convenience method for reading registry values + * NEW - `windows_auto_run` resource/provider + * NEW - `windows_env_vars` resource/provider + * NEW - `windows_path` resource/provider +* re-write of the `windows_package` logic for determining current installed packages +* new checksum attribute for `windows_package` resource...useful for remote packages + +## v1.0.2: + +* [COOK-647] account for Wow6432Node registry redirecter +* [COOK-656] begin/rescue on win32/registry + +## 1.0.0: + +* [COOK-612] initial release diff --git a/chef/cookbooks/windows/CONTRIBUTING b/chef/cookbooks/windows/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/windows/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/windows/LICENSE b/chef/cookbooks/windows/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/windows/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/windows/README.md b/chef/cookbooks/windows/README.md new file mode 100644 index 0000000..2829cbd --- /dev/null +++ b/chef/cookbooks/windows/README.md @@ -0,0 +1,609 @@ +Description +=========== + +Provides a set of Windows-specific primitives (Chef resources) meant to aid in the creation of cookbooks/recipes targeting the Windows platform. + +Requirements +============ + +Version 1.3.0+ of this cookbook requires Chef 0.10.10+. + +Platform +-------- + +* Windows XP +* Windows Vista +* Windows Server 2003 R2 +* Windows 7 +* Windows Server 2008 (R1, R2) + +The `windows_task` LWRP requires Windows Server 2008 due to its API usage. + +Cookbooks +--------- + +The following cookbooks provided by Opscode are required as noted: + +* chef_handler (`windows::reboot_handler` leverages the chef_handler LWRP) +* powershell - The Printer and Printer Port LWRP require Powershell. + +**NOTE** We cannot specifically depend on Opscode's powershell, + because powershell depends on this cookbook. Ensure that + `recipe[powershell]` exists in the node's expanded run list so it + gets downloaded where the printer LWRPs are used. + +Attributes +========== + +* `node['windows']['allow_pending_reboots']` - used to configure the `WindowsRebootHandler` (via the `windows::reboot_handler` recipe) to act on pending reboots. default is true (ie act on pending reboots). The value of this attribute only has an effect if the `windows::reboot_handler` is in a node's run list. + +Resource/Provider +================= + +windows\_auto\_run +------------------ + +### Actions + +- :create: Create an item to be run at login +- :remove: Remove an item that was previously setup to run at login + +### Attribute Parameters + +- :name: Name attribute. The name of the value to be stored in the registry +- :program: The program to be run at login +- :args: The arguments for the program + +### Examples + + # Run BGInfo at login + windows_auto_run 'BGINFO' do + program "C:/Sysinternals/bginfo.exe" + args "\"C:/Sysinternals/Config.bgi\" /NOLICPROMPT /TIMER:0" + not_if { Registry.value_exists?(AUTO_RUN_KEY, 'BGINFO') } + action :create + end + + +windows\_batch +-------------- + +Execute a batch script using the cmd.exe interpreter (much like the script resources for bash, csh, powershell, perl, python and ruby). A temporary file is created and executed like other script resources, rather than run inline. By their nature, Script resources are not idempotent, as they are completely up to the user's imagination. Use the `not_if` or `only_if` meta parameters to guard the resource for idempotence. + +### Actions + +- :run: run the batch file + +### Attribute Parameters + +- command: name attribute. Name of the command to execute. +- code: quoted string of code to execute. +- creates: a file this command creates - if the file exists, the command will not be run. +- cwd: current working directory to run the command from. +- flags: command line flags to pass to the interpreter when invoking. +- user: A user name or user ID that we should change to before running this command. +- group: A group name or group ID that we should change to before running this command. + +### Examples + + windows_batch "unzip_and_move_ruby" do + code <<-EOH + 7z.exe x #{Chef::Config[:file_cache_path]}/ruby-1.8.7-p352-i386-mingw32.7z -oC:\\source -r -y + xcopy C:\\source\\ruby-1.8.7-p352-i386-mingw32 C:\\ruby /e /y + EOH + end + + windows_batch "echo some env vars" do + code <<-EOH + echo %TEMP% + echo %SYSTEMDRIVE% + echo %PATH% + echo %WINDIR% + EOH + end + +windows\_feature +---------------- + +Windows Roles and Features can be thought of as built-in operating system packages that ship with the OS. A server role is a set of software programs that, when they are installed and properly configured, lets a computer perform a specific function for multiple users or other computers within a network. A Role can have multiple Role Services that provide functionality to the Role. Role services are software programs that provide the functionality of a role. Features are software programs that, although they are not directly parts of roles, can support or augment the functionality of one or more roles, or improve the functionality of the server, regardless of which roles are installed. Collectively we refer to all of these attributes as 'features'. + +This resource allows you to manage these 'features' in an unattended, idempotent way. + +There are two providers for the `windows_features` which map into Microsoft's two major tools for managing roles/features: [Deployment Image Servicing and Management (DISM)](http://msdn.microsoft.com/en-us/library/dd371719(v=vs.85).aspx) and [Servermanagercmd](http://technet.microsoft.com/en-us/library/ee344834(WS.10).aspx) (The CLI for Server Manager). As Servermanagercmd is deprecated, Chef will set the default provider to `Chef::Provider::WindowsFeature::DISM` if DISM is present on the system being configured. The default provider will fall back to `Chef::Provider::WindowsFeature::ServerManagerCmd`. + +For more information on Roles, Role Services and Features see the [Microsoft TechNet article on the topic](http://technet.microsoft.com/en-us/library/cc754923.aspx). For a complete list of all features that are available on a node type either of the following commands at a command prompt: + + dism /online /Get-Features + servermanagercmd -query + +### Actions + +- :install: install a Windows role/feature +- :remove: remove a Windows role/feature + +### Attribute Parameters + +- feature_name: name of the feature/role to install. The same feature may have different names depending on the provider used (ie DHCPServer vs DHCP; DNS-Server-Full-Role vs DNS). + +### Providers + +- **Chef::Provider::WindowsFeature::DISM**: Uses Deployment Image Servicing and Management (DISM) to manage roles/features. +- **Chef::Provider::WindowsFeature::ServerManagerCmd**: Uses Server Manager to manage roles/features. + +### Examples + + # enable the node as a DHCP Server + windows_feature "DHCPServer" do + action :install + end + + # enable TFTP + windows_feature "TFTP" do + action :install + end + + # disable Telnet client/server + %w{ TelnetServer TelnetClient }.each do |feature| + windows_feature feature do + action :remove + end + end + +windows\_package +---------------- + +Manage Windows application packages in an unattended, idempotent way. + +The following application installers are currently supported: + +* MSI packages +* InstallShield +* Wise InstallMaster +* Inno Setup +* Nullsoft Scriptable Install System + +If the proper installer type is not passed into the resource's installer_type attribute, the provider will do it's best to identify the type by introspecting the installation package. If the installation type cannot be properly identified the `:custom` value can be passed into the installer_type attribute along with the proper flags for silent/quiet installation (using the `options` attribute..see example below). + +__PLEASE NOTE__ - For proper idempotence the resource's `package_name` should be the same as the 'DisplayName' registry value in the uninstallation data that is created during package installation. The easiest way to definitively find the proper 'DisplayName' value is to install the package on a machine and search for the uninstall information under the following registry keys: + +* `HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall` +* `HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall` +* `HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall` + +For maximum flexibility the `source` attribute supports both remote and local installation packages. + +### Actions + +- :install: install a package +- :remove: remove a package. The remove action is completely hit or miss as many application uninstallers do not support a full silent/quiet mode. + +### Attribute Parameters + +- package_name: name attribute. The 'DisplayName' of the application installation package. +- source: The source of the windows installer. This can either be a URI or a local path. +- installer_type: They type of windows installation package. valid values are: :msi, :inno, :nsis, :wise, :installshield, :custom. If this value is not provided, the provider will do it's best to identify the installer type through introspection of the file. +- checksum: useful if source is remote, the SHA-256 checksum of the file--if the local file matches the checksum, Chef will not download it +- options: Additional options to pass the underlying installation command +- timeout: set a timeout for the package download (default 600 seconds) +- version: The version number of this package, as indicated by the 'DisplayVersion' value in one of the 'Uninstall' registry keys. If the given version number does equal the 'DisplayVersion' in the registry, the package will be installed. +- success_codes: set an array of possible successful installation + return codes. Previously this was hardcoded, but certain MSIs may + have a different return code, e.g. 3010 for reboot required. Must be + an array, and defaults to `[0, 42, 127]`. + +### Examples + + # install PuTTY (InnoSetup installer) + windows_package "PuTTY version 0.60" do + source "http://the.earth.li/~sgtatham/putty/latest/x86/putty-0.60-installer.exe" + installer_type :inno + action :install + end + + # install 7-Zip (MSI installer) + windows_package "7-Zip 9.20 (x64 edition)" do + source "http://downloads.sourceforge.net/sevenzip/7z920-x64.msi" + action :install + end + + # install Notepad++ (Y U No Emacs?) using a local installer + windows_package "Notepad++" do + source "c:/installation_files/npp.5.9.2.Installer.exe" + action :install + end + + # install VLC for that Xvid (NSIS installer) + windows_package "VLC media player 1.1.10" do + source "http://superb-sea2.dl.sourceforge.net/project/vlc/1.1.10/win32/vlc-1.1.10-win32.exe" + action :install + end + + # install Firefox as custom installer and manually set the silent install flags + windows_package "Mozilla Firefox 5.0 (x86 en-US)" do + source "http://archive.mozilla.org/pub/mozilla.org/mozilla.org/firefox/releases/5.0/win32/en-US/Firefox%20Setup%205.0.exe" + options "-ms" + installer_type :custom + action :install + end + + # Google Chrome FTW (MSI installer) + windows_package "Google Chrome" do + source "https://dl-ssl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B806F36C0-CB54-4A84-A3F3-0CF8A86575E0%7D%26lang%3Den%26browser%3D3%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dfalse/edgedl/chrome/install/GoogleChromeStandaloneEnterprise.msi" + action :install + end + + # remove Google Chrome (but why??) + windows_package "Google Chrome" do + action :remove + end + + # remove 7-Zip + windows_package "7-Zip 9.20 (x64 edition)" do + action :remove + end + + +windows\_printer\_port +---------------------- + +**Note** Include `recipe[powershell]` on the node's expanded run list + to ensure the powershell cookbook is downloaded to avoid circular + dependency. + +Create and delete TCP/IPv4 printer ports. + +### Actions + +- :create: Create a TCIP/IPv4 printer port. This is the default action. +- :delete: Delete a TCIP/IPv4 printer port + +### Attribute Parameters + +- :ipv4_address: Name attribute. Required. IPv4 address, e.g. "10.0.24.34" +- :port_name: Port name. Optional. Defaults to "IP_" + :ipv4_address +- :port_number: Port number. Optional. Defaults to 9100. +- :port_description: Port description. Optional. +- :snmp_enabled: Boolean. Optional. Defaults to false. +- :port_protocol: Port protocol, 1 (RAW), or 2 (LPR). Optional. Defaults to 1. + +### Examples + + # simplest example. Creates a TCP/IP printer port named "IP_10.4.64.37" + # with all defaults + windows_printer_port '10.4.64.37' do + end + + # delete a printer port + windows_printer_port '10.4.64.37' do + action :delete + end + + # delete a port with a custom port_name + windows_printer_port '10.4.64.38' do + port_name "My awesome port" + action :delete + end + + # Create a port with more options + windows_printer_port '10.4.64.39' do + port_name "My awesome port" + snmp_enabled true + port_protocol 2 + end + + +windows\_printer +---------------- + +**Note** Include `recipe[powershell]` on the node's expanded run list + to ensure the powershell cookbook is downloaded to avoid circular + dependency. + +Create Windows printer. Note that this doesn't currently install a printer +driver. You must already have the driver installed on the system. + +The Windows Printer LWRP will automatically create a TCP/IP printer port for you using the `ipv4_address` property. If you want more granular control over the printer port, just create it using the `windows_printer_port` LWRP before creating the printer. + +### Actions + +- :create: Create a new printer +- :delete: Delete a new printer + +### Attribute Parameters + +- :device_id: Name attribute. Required. Printer queue name, e.g. "HP LJ 5200 in fifth floor copy room" +- :comment: Optional string describing the printer queue. +- :default: Boolean. Optional. Defaults to false. Note that Windows sets the first printer defined to the default printer regardless of this setting. +- :driver_name: String. Required. Exact name of printer driver. Note that the printer driver must already be installed on the node. +- :location: Printer location, e.g. "Fifth floor copy room", or "US/NYC/Floor42/Room4207" +- :shared: Boolean. Defaults to false. +- :share_name: Printer share name. +- :ipv4_address: Printer IPv4 address, e.g. "10.4.64.23". You don't have to be able to ping the IP addresss to set it. Required. + + +### Examples + + # create a printer + windows_printer 'HP LaserJet 5th Floor' do + driver_name 'HP LaserJet 4100 Series PCL6' + ipv4_address '10.4.64.38' + end + + # delete a printer + # Note: this doesn't delete the associated printer port. + # See `windows_printer_port` above for how to delete the port. + windows_printer 'HP LaserJet 5th Floor' do + action :delete + end + + +windows\_reboot +--------------- + +Sets required data in the node's run_state to notify `WindowsRebootHandler` a reboot is requested. If Chef run completes successfully a reboot will occur if the `WindowsRebootHandler` is properly registered as a report handler. As an action of `:request` will cause a node to reboot every Chef run, this resource is usually notified by other resources...ie restart node after a package is installed (see example below). + +### Actions + +- :request: requests a reboot at completion of successful Cher run. requires `WindowsRebootHandler` to be registered as a report handler. +- :cancel: remove reboot request from node.run_state. this will cancel *ALL* previously requested reboots as this is a binary state. + +### Attribute Parameters + +- :timeout: Name attribute. timeout delay in seconds to wait before proceeding with the requested reboot. default is 60 seconds +- :reason: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot' + +### Examples + + # if the package installs, schedule a reboot at end of chef run + windows_reboot 60 do + reason 'cause chef said so' + action :nothing + end + windows_package 'some_package' do + action :install + notifies :request, 'windows_reboot[60]' + end + + # cancel the previously requested reboot + windows_reboot 60 do + action :cancel + end + +windows\_registry +----------------- + +Creates and modifies Windows registry keys. + +*Change in v1.3.0: The Win32 classes use `::Win32` to avoid namespace conflict with `Chef::Win32` (introduced in Chef 0.10.10).* + +### Actions + +- :create: create a new registry key with the provided values. +- :modify: modify an existing registry key with the provided values. +- :force_modify: modify an existing registry key with the provided values. ensures the value is actually set by checking multiple times. useful for fighting race conditions where two processes are trying to set the same registry key. This will be updated in the near future to use 'RegNotifyChangeKeyValue' which is exposed by the WinAPI and allows a process to register for notification on a registry key change. +- :remove: removes a value from an existing registry key + +### Attribute Parameters + +- key_name: name attribute. The registry key to create/modify. +- values: hash of the values to set under the registry key. The individual hash items will become respective 'Value name' => 'Value data' items in the registry key. +- type: Type of key to create, defaults to REG_SZ. Must be a symbol, see the overview below for valid values. + +### Registry key types + +- :binary: REG_BINARY +- :string: REG_SZ +- :multi_string: REG_MULTI_SZ +- :expand_string: REG_EXPAND_SZ +- :dword: REG_DWORD +- :dword_big_endian: REG_DWORD_BIG_ENDIAN +- :qword: REG_QWORD + +### Examples + + # make the local windows proxy match the one set for Chef + proxy = URI.parse(Chef::Config[:http_proxy]) + windows_registry 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings' do + values 'ProxyEnable' => 1, 'ProxyServer' => "#{proxy.host}:#{proxy.port}", 'ProxyOverride' => '' + end + + # enable Remote Desktop and poke the firewall hole + windows_registry 'HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server' do + values 'FdenyTSConnections' => 0 + end + + # Delete an item from the registry + windows_registry 'HKCU\Software\Test' do + #Key is the name of the value that you want to delete the value is always empty + values 'ValueToDelete' => '' + action :remove + end + + # Add a REG_MULTI_SZ value to the registry + windows_registry 'HKCU\Software\Test' do + values 'MultiString' => ['line 1', 'line 2', 'line 3'] + type :multi_string + end + +### Library Methods + + Registry.value_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') + Registry.key_exists?('HKLM\SOFTWARE\Microsoft') + BgInfo = Registry.get_value('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run','BGINFO') + +windows\_path +------------- + +### Actions + +- :add: Add an item to the system path +- :remove: Remove an item from the system path + +### Attribute Parameters + +- :path: Name attribute. The name of the value to add to the system path + +### Examples + + #Add Sysinternals to the system path + windows_path 'C:\Sysinternals' do + action :add + end + + #Remove 7-Zip from the system path + windows_path 'C:\7-Zip' do + action :remove + end + +windows\_task +------------- + +Creates, deletes or runs a Windows scheduled task. Requires Windows +Server 2008 due to API usage. + +### Actions + +- :create: creates a task +- :delete: deletes a task +- :run: runs a task +- :change: changes the un/pw or command of a task + +### Attribute Parameters + +- name: name attribute, The task name. +- command: The command the task will run. +- cwd: The directory the task will be run from. +- user: The user to run the task as. (requires password) +- password: The user's password. (requires user) +- run_level: Run with limited or highest privileges. +- frequency: Frequency with which to run the task. (hourly, daily, ect.) +- frequency_modifier: Multiple for frequency. (15 minutes, 2 days) + +### Examples + + # Run Chef every 15 minutes + windows_task "Chef client" do + user "Administrator" + password "$ecR3t" + cwd "C:\chef\bin" + command "chef-client -L C:\tmp\" + run_level :highest + frequency :minute + frequency_modifier 15 + end + + # Update Chef Client task with new password and log location + windows_task "Chef client" do + user "Administrator" + password "N3wPassW0Rd" + cwd "C:\chef\bin" + command "chef-client -L C:\chef\logs\" + action :change + end + + # Delete a taks named "old task" + windows_task "old task" do + action :delete + end + +windows\_zipfile +---------------- + +Most version of Windows do not ship with native cli utility for managing compressed files. This resource provides a pure-ruby implementation for managing zip files. Be sure to use the `not_if` or `only_if` meta parameters to guard the resource for idempotence or action will be taken on the zip file every Chef run. + +### Actions + +- :unzip: unzip a compressed file + +### Attribute Parameters + +- path: name attribute. The path where files will be unzipped to. +- source: The source of the zip file. This can either be a URI or a local path. +- overwrite: force an overwrite of the files if the already exists. +- checksum: useful if source is remote, the SHA-256 checksum of the file--if the local file matches the checksum, Chef will not download it + +### Examples + + # unzip a remote zip file locally + windows_zipfile "c:/bin" do + source "http://download.sysinternals.com/Files/SysinternalsSuite.zip" + action :unzip + not_if {::File.exists?("c:/bin/PsExec.exe")} + end + + # unzip a local zipfile + windows_zipfile "c:/the_codez" do + source "c:/foo/baz/the_codez.zip" + action :unzip + end + + +Exception/Report Handlers +========================= + +WindowsRebootHandler +-------------------- + +Required reboots are a necessary evil of configuring and managing Windows nodes. This report handler (ie fires at the end of successful Chef runs) acts on requested (Chef initiated) or pending (as determined by the OS per configuration action we performed) reboots. The `allow_pending_reboots` initialization argument should be set to false if you do not want the handler to automatically reboot a node if it has been determined a reboot is pending. Reboots can still be requested explicitly via the `windows_reboot` LWRP. + +## Initialization Arguments + +- `allow_pending_reboots`: indicator on whether the handler should act on a the Window's 'pending reboot' state. default is true +- `timeout`: timeout delay in seconds to wait before proceeding with the reboot. default is 60 seconds +- `reason`: comment on the reason for the reboot. default is 'Opscode Chef initiated reboot' + +Usage +===== + +Place an explicit dependency on this cookbook (using depends in the cookbook's metadata.rb) from any cookbook where you would like to use the Windows-specific resources/providers that ship with this cookbook. + + depends "windows" + +default +------- + +Convenience recipe that installs supporting gems for many of the resources/providers that ship with this cookbook. + +*Change in v1.3.0: Uses chef_gem instead of gem_package to ensure gem installation in Chef 0.10.10.* + +reboot\_handler +-------------- + +Leverages the `chef_handler` LWRP to register the `WindowsRebootHandler` report handler that ships as part of this cookbook. By default this handler is set to automatically act on pending reboots. If you would like to change this behavior override `node['windows']['allow_pending_reboots']` and set the value to false. For example: + + % cat roles/base.rb + name "base" + description "base role" + override_attributes( + "windows" => { + "allow_pending_reboots" => false + } + ) + +This will still allow a reboot to be explicitly requested via the `windows_reboot` LWRP. + +License and Author +================== + +Author:: Seth Chisamore () +Author:: Doug MacEachern () +Author:: Paul Morton () +Author:: Doug Ireton () + +Copyright:: 2011, Opscode, Inc. +Copyright:: 2010, VMware, Inc. +Copyright:: 2011, Business Intelligence Associates, Inc +Copyright:: 2012, Nordstrom, Inc. + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/windows/attributes/default.rb b/chef/cookbooks/windows/attributes/default.rb new file mode 100644 index 0000000..c4e5919 --- /dev/null +++ b/chef/cookbooks/windows/attributes/default.rb @@ -0,0 +1,23 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Attribute:: default +# +# Copyright 2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['windows']['allow_pending_reboots'] = true +default['windows']['rubyzipversion'] = nil +default['windows']['reboot_timeout'] = 60 \ No newline at end of file diff --git a/chef/cookbooks/windows/files/default/handlers/windows_reboot_handler.rb b/chef/cookbooks/windows/files/default/handlers/windows_reboot_handler.rb new file mode 100644 index 0000000..8875ae4 --- /dev/null +++ b/chef/cookbooks/windows/files/default/handlers/windows_reboot_handler.rb @@ -0,0 +1,76 @@ +# +# Author:: Seth Chisamore () +# Copyright:: Copyright (c) 2011 Opscode, Inc +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class WindowsRebootHandler < Chef::Handler + include Chef::Mixin::ShellOut + + def initialize(allow_pending_reboots = true, timeout = 60, reason = "Opscode Chef initiated reboot") + @allow_pending_reboots = allow_pending_reboots + @timeout = timeout + @reason = reason + end + + def report + log_message, reboot = begin + if reboot_requested? + ["chef_handler[#{self.class}] requested reboot will occur in #{timeout} seconds", true] + elsif reboot_pending? + if @allow_pending_reboots + ["chef_handler[#{self.class}] reboot pending - automatic reboot will occur in #{timeout} seconds", true] + else + ["chef_handler[#{self.class}] reboot pending but handler not configured to act on pending reboots - please reboot node manually", false] + end + else + ["chef_handler[#{self.class}] no reboot requested or pending", false] + end + end + + Chef::Log.warn(log_message) + shell_out!("shutdown /r /t #{timeout} /c \"#{reason}\"") if reboot + end + + private + # reboot cause CHEF says so: + # reboot explicitly requested in our cookbook code + def reboot_requested? + node.run_state[:reboot_requested] == true + end + + # reboot cause WIN says so: + # reboot pending because of some configuration action we performed + def reboot_pending? + # Any files listed here means reboot needed + (Registry.key_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations') && + Registry.get_value('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager','PendingFileRenameOperations').any?) || + # 1 for any value means reboot pending + # "9306cdfc-c4a1-4a22-9996-848cb67eddc3"=1 + (Registry.key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') && + Registry.get_values('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired').select{|v| v[2] == 1 }.any?) || + # 1 or 2 for 'Flags' value means reboot pending + (Registry.key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') && + [1,2].include?(Registry::get_value('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile','Flags'))) + end + + def timeout + node.run_state[:reboot_timeout] || node['windows']['reboot_timeout'] || @timeout + end + + def reason + node.run_state[:reboot_reason] || @reason + end +end \ No newline at end of file diff --git a/chef/cookbooks/windows/libraries/feature_base.rb b/chef/cookbooks/windows/libraries/feature_base.rb new file mode 100644 index 0000000..66cbc42 --- /dev/null +++ b/chef/cookbooks/windows/libraries/feature_base.rb @@ -0,0 +1,41 @@ +class Chef + class Provider + class WindowsFeature + module Base + + def action_install + unless installed? + install_feature(@new_resource.feature_name) + @new_resource.updated_by_last_action(true) + Chef::Log.info("#{@new_resource} installed feature") + else + Chef::Log.debug("#{@new_resource} is already installed - nothing to do") + end + end + + def action_remove + if installed? + remove_feature(@new_resource.feature_name) + @new_resource.updated_by_last_action(true) + Chef::Log.info("#{@new_resource} removed") + else + Chef::Log.debug("#{@new_resource} feature does not exist - nothing to do") + end + end + + def install_feature(name) + raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :install" + end + + def remove_feature(name) + raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remove" + end + + def installed? + raise Chef::Exceptions::Override, "You must override installed? in #{self.to_s}" + end + end + end + end +end + \ No newline at end of file diff --git a/chef/cookbooks/windows/libraries/helper.rb b/chef/cookbooks/windows/libraries/helper.rb new file mode 100644 index 0000000..821d3df --- /dev/null +++ b/chef/cookbooks/windows/libraries/helper.rb @@ -0,0 +1,88 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Library:: helper +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require 'uri' + +module Windows + module Helper + + AUTO_RUN_KEY = 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'.freeze unless defined?(AUTO_RUN_KEY) + ENV_KEY = 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'.freeze unless defined?(ENV_KEY) + + # returns windows friendly version of the provided path, + # ensures backslashes are used everywhere + def win_friendly_path(path) + path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR) if path + end + + # account for Window's wacky File System Redirector + # http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx + # especially important for 32-bit processes (like Ruby) on a + # 64-bit instance of Windows. + def locate_sysnative_cmd(cmd) + if ::File.exists?("#{ENV['WINDIR']}\\sysnative\\#{cmd}") + "#{ENV['WINDIR']}\\sysnative\\#{cmd}" + elsif ::File.exists?("#{ENV['WINDIR']}\\system32\\#{cmd}") + "#{ENV['WINDIR']}\\system32\\#{cmd}" + else + cmd + end + end + + # Create a feature provider dependent value object. + # mainly created becasue Windows Feature names are + # different based on whether dism.exe or servicemanagercmd.exe + # is used for installation + def value_for_feature_provider(provider_hash) + p = Chef::Platform.find_provider_for_node(node, :windows_feature) + key = p.to_s.downcase.split('::').last + provider_hash[key] || provider_hash[key.to_sym] + end + + # singleton instance of the Windows Version checker + def win_version + @win_version ||= Windows::Version.new + end + + # if a file is local it returns a windows friendly path version + # if a file is remote it caches it locally + def cached_file(source, checksum=nil, windows_path=true) + @installer_file_path ||= begin + + if source =~ ::URI::ABS_URI && %w[http https].include?(URI.parse(source).scheme) + uri = ::URI.parse(::URI.unescape(source)) + cache_file_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(uri.path)}" + Chef::Log.debug("Caching a copy of file #{source} at #{cache_file_path}") + r = Chef::Resource::RemoteFile.new(cache_file_path, run_context) + r.source(source) + r.backup(false) + r.checksum(checksum) if checksum + r.run_action(:create) + else + cache_file_path = source + end + + windows_path ? win_friendly_path(cache_file_path) : cache_file_path + end + end + + end +end + +Chef::Recipe.send(:include, Windows::Helper) diff --git a/chef/cookbooks/windows/libraries/registry_helper.rb b/chef/cookbooks/windows/libraries/registry_helper.rb new file mode 100644 index 0000000..8daf041 --- /dev/null +++ b/chef/cookbooks/windows/libraries/registry_helper.rb @@ -0,0 +1,357 @@ +# +# Author:: Doug MacEachern () +# Author:: Seth Chisamore () +# Author:: Paul Morton () +# Cookbook Name:: windows +# Provider:: registry +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Opscode, Inc. +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + require 'win32/registry' + require 'ruby-wmi' +end + +module Windows + module RegistryHelper + + @@native_registry_constant = ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' ? 0x0100 : 0x0200 + + def get_hive_name(path) + Chef::Log.debug("Resolving registry shortcuts to full names") + + reg_path = path.split("\\") + hive_name = reg_path.shift + + hkey = { + "HKLM" => "HKEY_LOCAL_MACHINE", + "HKCU" => "HKEY_CURRENT_USER", + "HKU" => "HKEY_USERS" + }[hive_name] || hive_name + + Chef::Log.debug("Hive resolved to #{hkey}") + return hkey + end + + def get_hive(path) + + Chef::Log.debug("Getting hive for #{path}") + reg_path = path.split("\\") + hive_name = reg_path.shift + + hkey = get_hive_name(path) + + hive = { + "HKEY_LOCAL_MACHINE" => ::Win32::Registry::HKEY_LOCAL_MACHINE, + "HKEY_USERS" => ::Win32::Registry::HKEY_USERS, + "HKEY_CURRENT_USER" => ::Win32::Registry::HKEY_CURRENT_USER + }[hkey] + + unless hive + Chef::Application.fatal!("Unsupported registry hive '#{hive_name}'") + end + + + Chef::Log.debug("Registry hive resolved to #{hkey}") + return hive + end + + def unload_hive(path) + hive = get_hive(path) + if hive == ::Win32::Registry::HKEY_USERS + reg_path = path.split("\\") + priv = Chef::WindowsPrivileged.new + begin + priv.reg_unload_key(reg_path[1]) + rescue + end + end + end + + def set_value(mode,path,values,type=nil) + hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path) + key_name = reg_path.join("\\") + + Chef::Log.debug("Creating #{path}") + + if !key_exists?(path,true) + create_key(path) + end + + hive.send(mode, key_name, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg| + changed_something = false + values.each do |k,val| + key = k.to_s #wtf. avoid "can't modify frozen string" in win32/registry.rb + cur_val = nil + begin + cur_val = reg[key] + rescue + #subkey does not exist (ok) + end + if cur_val != val + Chef::Log.debug("setting #{key}=#{val}") + + if type.nil? + type = :string + end + + reg_type = { + :binary => ::Win32::Registry::REG_BINARY, + :string => ::Win32::Registry::REG_SZ, + :multi_string => ::Win32::Registry::REG_MULTI_SZ, + :expand_string => ::Win32::Registry::REG_EXPAND_SZ, + :dword => ::Win32::Registry::REG_DWORD, + :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN, + :qword => ::Win32::Registry::REG_QWORD + }[type] + + reg.write(key, reg_type, val) + + ensure_hive_unloaded(hive_loaded) + + changed_something = true + end + end + return changed_something + end + return false + end + + def get_value(path,value) + hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + + hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do | reg | + begin + return reg[value] + rescue + return nil + ensure + ensure_hive_unloaded(hive_loaded) + end + end + end + + def get_values(path) + hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do | reg | + values = [] + begin + reg.each_value do |name, type, data| + values << [name, type, data] + end + rescue + ensure + ensure_hive_unloaded(hive_loaded) + end + values + end + end + + def delete_value(path,values) + hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + Chef::Log.debug("Deleting values in #{path}") + hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do | reg | + values.each_key { |key| + name = key.to_s + # Ensure delete operation is idempotent. + if value_exists?(path, key) + Chef::Log.debug("Deleting value #{name} in #{path}") + reg.delete_value(name) + else + Chef::Log.debug("Value #{name} in #{path} does not exist, skipping.") + end + } + end + + end + + def create_key(path) + hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + Chef::Log.debug("Creating registry key #{path}") + hive.create(key) + end + + def value_exists?(path,value) + if key_exists?(path,true) + + hive, reg_path, hive_name, root_key , hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + + Chef::Log.debug("Attempting to open #{key}"); + Chef::Log.debug("Native Constant #{@@native_registry_constant}") + Chef::Log.debug("Hive #{hive}") + + hive.open(key, ::Win32::Registry::KEY_READ | @@native_registry_constant) do | reg | + begin + rtn_value = reg[value] + return true + rescue + return false + ensure + ensure_hive_unloaded(hive_loaded) + end + end + + end + return false + end + + # TODO: Does not load user registry... + def key_exists?(path, load_hive = false) + if load_hive + hive, reg_path, hive_name, root_key , hive_loaded = get_reg_path_info(path) + key = reg_path.join("\\") + else + hive = get_hive(path) + reg_path = path.split("\\") + hive_name = reg_path.shift + root_key = reg_path[0] + key = reg_path.join("\\") + hive_loaded = false + end + + begin + hive.open(key, ::Win32::Registry::Constants::KEY_READ | @@native_registry_constant ) + return true + rescue + return false + ensure + ensure_hive_unloaded(hive_loaded) + end + end + + def get_user_hive_location(sid) + reg_key = "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}" + Chef::Log.debug("Looking for profile at #{reg_key}") + if key_exists?(reg_key) + return get_value(reg_key,'ProfileImagePath') + else + return nil + end + + end + + def resolve_user_to_sid(username) + begin + sid = WMI::Win32_UserAccount.find(:first, :conditions => {:name => username}).sid + Chef::Log.debug("Resolved user SID to #{sid}") + return sid + rescue + return nil + end + end + + def hive_loaded?(path) + hive = get_hive(path) + reg_path = path.split("\\") + hive_name = reg_path.shift + user_hive = path[0] + + if is_user_hive?(hive) + return key_exists?("#{hive_name}\\#{user_hive}") + else + return true + end + end + + def is_user_hive?(hive) + if hive == ::Win32::Registry::HKEY_USERS + return true + else + return true + end + end + + def get_reg_path_info(path) + hive = get_hive(path) + reg_path = path.split("\\") + hive_name = reg_path.shift + root_key = reg_path[0] + hive_loaded = false + + if is_user_hive?(hive) && !key_exists?("#{hive_name}\\#{root_key}") + reg_path, hive_loaded = load_user_hive(hive,reg_path,root_key) + root_key = reg_path[0] + Chef::Log.debug("Resolved user (#{path}) to (#{reg_path.join('/')})") + end + + return hive, reg_path, hive_name, root_key, hive_loaded + end + + def load_user_hive(hive,reg_path,user_hive) + Chef::Log.debug("Reg Path #{reg_path}") + # See if the hive is loaded. Logged in users will have a key that is named their SID + # if the user has specified the a path by SID and the user is logged in, this function + # should not be executed. + if is_user_hive?(hive) && !key_exists?("HKU\\#{user_hive}") + Chef::Log.debug("The user is not logged in and has not been specified by SID") + sid = resolve_user_to_sid(user_hive) + Chef::Log.debug("User SID resolved to (#{sid})") + # Now that the user has been resolved to a SID, check and see if the hive exists. + # If this exists by SID, the user is logged in and we should use that key. + # TODO: Replace the username with the sid and send it back because the username + # does not exist as the key location. + load_reg = false + if key_exists?("HKU\\#{sid}") + reg_path[0] = sid #use the active profile (user is logged on) + Chef::Log.debug("HKEY_USERS Mapped: #{user_hive} -> #{sid}") + else + Chef::Log.debug("User is not logged in") + load_reg = true + end + + # The user is not logged in, so we should load the registry from disk + if load_reg + profile_path = get_user_hive_location(sid) + if profile_path != nil + ntuser_dat = "#{profile_path}\\NTUSER.DAT" + if ::File.exists?(ntuser_dat) + priv = Chef::WindowsPrivileged.new + if priv.reg_load_key(sid,ntuser_dat) + Chef::Log.debug("RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})") + reg_path[0] = sid + else + Chef::Log.debug("Failed RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})") + end + end + end + end + end + + return reg_path, load_reg + + end + + private + def ensure_hive_unloaded(hive_loaded=false) + if(hive_loaded) + Chef::Log.debug("Hive was loaded, we really should unload it") + unload_hive(path) + end + end + end +end + +module Registry + module_function + extend Windows::RegistryHelper +end diff --git a/chef/cookbooks/windows/libraries/version.rb b/chef/cookbooks/windows/libraries/version.rb new file mode 100644 index 0000000..512e14a --- /dev/null +++ b/chef/cookbooks/windows/libraries/version.rb @@ -0,0 +1,207 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Library:: version +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + require 'ruby-wmi' + require 'Win32API' +end + +module Windows + class Version + + # http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx + + # Suite Masks + # Microsoft BackOffice components are installed. + VER_SUITE_BACKOFFICE = 0x00000004.freeze unless defined?(VER_SUITE_BACKOFFICE) + # Windows Server 2003, Web Edition is installed. + VER_SUITE_BLADE = 0x00000400.freeze unless defined?(VER_SUITE_BLADE) + # Windows Server 2003, Compute Cluster Edition is installed. + VER_SUITE_COMPUTE_SERVER = 0x00004000.freeze unless defined?(VER_SUITE_COMPUTE_SERVER) + # Windows Server 2008 Datacenter, Windows Server 2003, Datacenter Edition, or Windows 2000 Datacenter Server is installed. + VER_SUITE_DATACENTER = 0x00000080.freeze unless defined?(VER_SUITE_DATACENTER) + # Windows Server 2008 Enterprise, Windows Server 2003, Enterprise Edition, or Windows 2000 Advanced Server is installed. Refer to the Remarks section for more information about this bit flag. + VER_SUITE_ENTERPRISE = 0x00000002.freeze unless defined?(VER_SUITE_ENTERPRISE) + # Windows XP Embedded is installed. + VER_SUITE_EMBEDDEDNT = 0x00000040.freeze unless defined?(VER_SUITE_EMBEDDEDNT) + # Windows Vista Home Premium, Windows Vista Home Basic, or Windows XP Home Edition is installed. + VER_SUITE_PERSONAL = 0x00000200.freeze unless defined?(VER_SUITE_PERSONAL) + # Remote Desktop is supported, but only one interactive session is supported. This value is set unless the system is running in application server mode. + VER_SUITE_SINGLEUSERTS = 0x00000100.freeze unless defined?(VER_SUITE_SINGLEUSERTS) + # Microsoft Small Business Server was once installed on the system, but may have been upgraded to another version of Windows. Refer to the Remarks section for more information about this bit flag. + VER_SUITE_SMALLBUSINESS = 0x00000001.freeze unless defined?(VER_SUITE_SMALLBUSINESS) + # Microsoft Small Business Server is installed with the restrictive client license in force. Refer to the Remarks section for more information about this bit flag. + VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x00000020.freeze unless defined?(VER_SUITE_SMALLBUSINESS_RESTRICTED) + # Windows Storage Server 2003 R2 or Windows Storage Server 2003is installed. + VER_SUITE_STORAGE_SERVER = 0x00002000.freeze unless defined?(VER_SUITE_STORAGE_SERVER) + # Terminal Services is installed. This value is always set. + # If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server mode. + VER_SUITE_TERMINAL = 0x00000010.freeze unless defined?(VER_SUITE_TERMINAL) + # Windows Home Server is installed. + VER_SUITE_WH_SERVER = 0x00008000.freeze unless defined?(VER_SUITE_WH_SERVER) + + # Product Type + # The system is a domain controller and the operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server. + VER_NT_DOMAIN_CONTROLLER = 0x0000002.freeze unless defined?(VER_NT_DOMAIN_CONTROLLER) + # The operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server. + # Note that a server that is also a domain controller is reported as VER_NT_DOMAIN_CONTROLLER, not VER_NT_SERVER. + VER_NT_SERVER = 0x0000003.freeze unless defined?(VER_NT_SERVER) + # The operating system is Windows 7, Windows Vista, Windows XP Professional, Windows XP Home Edition, or Windows 2000 Professional. + VER_NT_WORKSTATION = 0x0000001.freeze unless defined?(VER_NT_WORKSTATION) + + # GetSystemMetrics + # The build number if the system is Windows Server 2003 R2; otherwise, 0. + SM_SERVERR2 = 89.freeze unless defined?(SM_SERVERR2) + + # http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx + # this is what it sounds like...when kittens die + SKU = { + 0x00000006 => {:ms_const => 'PRODUCT_BUSINESS', :name => 'Business'}, + 0x00000010 => {:ms_const => 'PRODUCT_BUSINESS_N', :name => 'Business N'}, + 0x00000012 => {:ms_const => 'PRODUCT_CLUSTER_SERVER', :name => 'HPC Edition'}, + 0x00000008 => {:ms_const => 'PRODUCT_DATACENTER_SERVER', :name => 'Server Datacenter (full installation)'}, + 0x0000000C => {:ms_const => 'PRODUCT_DATACENTER_SERVER_CORE', :name => 'Server Datacenter (core installation)'}, + 0x00000027 => {:ms_const => 'PRODUCT_DATACENTER_SERVER_CORE_V', :name => 'Server Datacenter without Hyper-V (core installation)'}, + 0x00000025 => {:ms_const => 'PRODUCT_DATACENTER_SERVER_V', :name => 'Server Datacenter without Hyper-V (full installation)'}, + 0x00000004 => {:ms_const => 'PRODUCT_ENTERPRISE', :name => 'Enterprise'}, + 0x00000046 => {:ms_const => 'PRODUCT_ENTERPRISE_E', :name => 'Not supported'}, + 0x0000001B => {:ms_const => 'PRODUCT_ENTERPRISE_N', :name => 'Enterprise N'}, + 0x0000000A => {:ms_const => 'PRODUCT_ENTERPRISE_SERVER', :name => 'Server Enterprise (full installation)'}, + 0x0000000E => {:ms_const => 'PRODUCT_ENTERPRISE_SERVER_CORE', :name => 'Server Enterprise (core installation)'}, + 0x00000029 => {:ms_const => 'PRODUCT_ENTERPRISE_SERVER_CORE_V', :name => 'Server Enterprise without Hyper-V (core installation)'}, + 0x0000000F => {:ms_const => 'PRODUCT_ENTERPRISE_SERVER_IA64', :name => 'Server Enterprise for Itanium-based Systems'}, + 0x00000026 => {:ms_const => 'PRODUCT_ENTERPRISE_SERVER_V', :name => 'Server Enterprise without Hyper-V (full installation)'}, + 0x00000002 => {:ms_const => 'PRODUCT_HOME_BASIC', :name => 'Home Basic'}, + 0x00000043 => {:ms_const => 'PRODUCT_HOME_BASIC_E', :name => 'Not supported'}, + 0x00000005 => {:ms_const => 'PRODUCT_HOME_BASIC_N', :name => 'Home Basic N'}, + 0x00000003 => {:ms_const => 'PRODUCT_HOME_PREMIUM', :name => 'Home Premium'}, + 0x00000044 => {:ms_const => 'PRODUCT_HOME_PREMIUM_E', :name => 'Not supported'}, + 0x0000001A => {:ms_const => 'PRODUCT_HOME_PREMIUM_N', :name => 'Home Premium N'}, + 0x0000002A => {:ms_const => 'PRODUCT_HYPERV', :name => 'Microsoft Hyper-V Server'}, + 0x0000001E => {:ms_const => 'PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT', :name => 'Windows Essential Business Server Management Server'}, + 0x00000020 => {:ms_const => 'PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING', :name => 'Windows Essential Business Server Messaging Server'}, + 0x0000001F => {:ms_const => 'PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY', :name => 'Windows Essential Business Server Security Server'}, + 0x00000030 => {:ms_const => 'PRODUCT_PROFESSIONAL', :name => 'Professional'}, + 0x00000045 => {:ms_const => 'PRODUCT_PROFESSIONAL_E', :name => 'Not supported'}, + 0x00000031 => {:ms_const => 'PRODUCT_PROFESSIONAL_N', :name => 'Professional N'}, + 0x00000018 => {:ms_const => 'PRODUCT_SERVER_FOR_SMALLBUSINESS', :name => 'Windows Server 2008 for Windows Essential Server Solutions'}, + 0x00000023 => {:ms_const => 'PRODUCT_SERVER_FOR_SMALLBUSINESS_V', :name => 'Windows Server 2008 without Hyper-V for Windows Essential Server Solutions'}, + 0x00000021 => {:ms_const => 'PRODUCT_SERVER_FOUNDATION', :name => 'Server Foundation'}, + 0x00000022 => {:ms_const => 'PRODUCT_HOME_PREMIUM_SERVER', :name => 'Windows Home Server 2011'}, + 0x00000032 => {:ms_const => 'PRODUCT_SB_SOLUTION_SERVER', :name => 'Windows Small Business Server 2011 Essentials'}, + 0x00000013 => {:ms_const => 'PRODUCT_HOME_SERVER', :name => 'Windows Storage Server 2008 R2 Essentials'}, + 0x00000009 => {:ms_const => 'PRODUCT_SMALLBUSINESS_SERVER', :name => 'Windows Small Business Server'}, + 0x00000038 => {:ms_const => 'PRODUCT_SOLUTION_EMBEDDEDSERVER', :name => 'Windows MultiPoint Server'}, + 0x00000007 => {:ms_const => 'PRODUCT_STANDARD_SERVER', :name => 'Server Standard (full installation)'}, + 0x0000000D => {:ms_const => 'PRODUCT_STANDARD_SERVER_CORE', :name => 'Server Standard (core installation)'}, + 0x00000028 => {:ms_const => 'PRODUCT_STANDARD_SERVER_CORE_V', :name => 'Server Standard without Hyper-V (core installation)'}, + 0x00000024 => {:ms_const => 'PRODUCT_STANDARD_SERVER_V', :name => 'Server Standard without Hyper-V (full installation)'}, + 0x0000000B => {:ms_const => 'PRODUCT_STARTER', :name => 'Starter'}, + 0x00000042 => {:ms_const => 'PRODUCT_STARTER_E', :name => 'Not supported'}, + 0x0000002F => {:ms_const => 'PRODUCT_STARTER_N', :name => 'Starter N'}, + 0x00000017 => {:ms_const => 'PRODUCT_STORAGE_ENTERPRISE_SERVER', :name => 'Storage Server Enterprise'}, + 0x00000014 => {:ms_const => 'PRODUCT_STORAGE_EXPRESS_SERVER', :name => 'Storage Server Express'}, + 0x00000015 => {:ms_const => 'PRODUCT_STORAGE_STANDARD_SERVER', :name => 'Storage Server Standard'}, + 0x00000016 => {:ms_const => 'PRODUCT_STORAGE_WORKGROUP_SERVER', :name => 'Storage Server Workgroup'}, + 0x00000000 => {:ms_const => 'PRODUCT_UNDEFINED', :name => 'An unknown product'}, + 0x00000001 => {:ms_const => 'PRODUCT_ULTIMATE', :name => 'Ultimate'}, + 0x00000047 => {:ms_const => 'PRODUCT_ULTIMATE_E', :name => 'Not supported'}, + 0x0000001C => {:ms_const => 'PRODUCT_ULTIMATE_N', :name => 'Ultimate N'}, + 0x00000011 => {:ms_const => 'PRODUCT_WEB_SERVER', :name => 'Web Server (full installation)'}, + 0x0000001D => {:ms_const => 'PRODUCT_WEB_SERVER_CORE', :name => 'Web Server (core installation)'} + }.freeze unless defined?(SKU) + + attr_reader :major_version, :minor_version, :build_number, :service_pack_major_version, :service_pack_minor_version + attr_reader :version, :product_type, :product_suite, :sku + + def initialize + unless RUBY_PLATFORM =~ /mswin|mingw32|windows/ + raise NotImplementedError, 'only valid on Windows platform' + end + @version, @product_type, @product_suite, @sku, @service_pack_major_version, @service_pack_minor_version = get_os_info + @major_version, @minor_version, @build_number = version.split('.').map{|v| v.to_i } + end + + WIN_VERSIONS = { + "Windows Server 2012 R2" => {:major => 6, :minor => 3, :callable => lambda{ @product_type != VER_NT_WORKSTATION }}, + "Windows 8" => {:major => 6, :minor => 2, :callable => lambda{ @product_type == VER_NT_WORKSTATION }}, + "Windows Server 2012" => {:major => 6, :minor => 2, :callable => lambda{ @product_type != VER_NT_WORKSTATION }}, + "Windows 7" => {:major => 6, :minor => 1, :callable => lambda{ @product_type == VER_NT_WORKSTATION }}, + "Windows Server 2008 R2" => {:major => 6, :minor => 1, :callable => lambda{ @product_type != VER_NT_WORKSTATION }}, + "Windows Server 2008" => {:major => 6, :minor => 0, :callable => lambda{ @product_type != VER_NT_WORKSTATION }}, + "Windows Vista" => {:major => 6, :minor => 0, :callable => lambda{ @product_type == VER_NT_WORKSTATION }}, + "Windows Server 2003 R2" => {:major => 5, :minor => 2, :callable => lambda{ Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) != 0 }}, + "Windows Home Server" => {:major => 5, :minor => 2, :callable => lambda{ (@product_suite & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER }}, + "Windows Server 2003" => {:major => 5, :minor => 2, :callable => lambda{ Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) == 0 }}, + "Windows XP" => {:major => 5, :minor => 1}, + "Windows 2000" => {:major => 5, :minor => 0} + }.freeze unless defined?(WIN_VERSIONS) + + marketing_names = Array.new + + # General Windows checks + WIN_VERSIONS.each do |k,v| + method_name = "#{k.gsub(/\s/, '_').downcase}?" + define_method(method_name) do + (@major_version == v[:major]) && + (@minor_version == v[:minor]) && + (v[:callable] ? v[:callable].call : true) + end + marketing_names << [k, method_name] + end + + define_method(:marketing_name) do + marketing_names.each do |mn| + break mn[0] if self.send(mn[1]) + end + end + + # Server Type checks + %w{ core full datacenter }.each do |m| + define_method("server_#{m}?") do + if @sku + !(SKU[@sku][:name] =~ /#{m}/i).nil? + else + false + end + end + end + + private + # Win32API call to GetSystemMetrics(SM_SERVERR2) + # returns: The build number if the system is Windows Server 2003 R2; otherwise, 0. + def sm_serverr2 + @sm_serverr2 ||= Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) + end + + # query WMI Win32_OperatingSystem for required OS info + def get_os_info + cols = %w{ Version ProductType OSProductSuite OperatingSystemSKU ServicePackMajorVersion ServicePackMinorVersion } + os_info = WMI::Win32_OperatingSystem.find(:first) + cols.map do |c| + begin + os_info.send(c) + rescue # OperatingSystemSKU doesn't exist in all versions of Windows + nil + end + end + end + end +end diff --git a/chef/cookbooks/windows/libraries/windows_privileged.rb b/chef/cookbooks/windows/libraries/windows_privileged.rb new file mode 100644 index 0000000..f868835 --- /dev/null +++ b/chef/cookbooks/windows/libraries/windows_privileged.rb @@ -0,0 +1,94 @@ +# +# Author:: Doug MacEachern +# Author:: Paul Morton () +# Cookbook Name:: windows +# Library:: windows_privileged +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + require 'windows/error' + require 'windows/registry' + require 'windows/process' + require 'windows/security' +end + +#helpers for Windows API calls that require privilege adjustments +class Chef + class WindowsPrivileged + if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + include Windows::Error + include Windows::Registry + include Windows::Process + include Windows::Security + end + #File -> Load Hive... in regedit.exe + def reg_load_key(name, file) + run(SE_BACKUP_NAME, SE_RESTORE_NAME) do + rc = RegLoadKey(HKEY_USERS, name.to_s, file) + if rc == ERROR_SUCCESS + return true + elsif rc == ERROR_SHARING_VIOLATION + return false + else + raise get_last_error(rc) + end + end + end + + #File -> Unload Hive... in regedit.exe + def reg_unload_key(name) + run(SE_BACKUP_NAME, SE_RESTORE_NAME) do + rc = RegUnLoadKey(HKEY_USERS, name.to_s) + if rc != ERROR_SUCCESS + raise get_last_error(rc) + end + end + end + + def run(*privileges) + token = [0].pack('L') + + unless OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, token) + raise get_last_error + end + token = token.unpack('L')[0] + + privileges.each do |name| + unless adjust_privilege(token, name, SE_PRIVILEGE_ENABLED) + raise get_last_error + end + end + + begin + yield + ensure #disable privs + privileges.each do |name| + adjust_privilege(token, name, 0) + end + end + end + + def adjust_privilege(token, priv, attr=0) + luid = [0,0].pack('Ll') + if LookupPrivilegeValue(nil, priv, luid) + new_state = [1, luid.unpack('Ll'), attr].flatten.pack('LLlL') + AdjustTokenPrivileges(token, 0, new_state, new_state.size, 0, 0) + end + end + end +end diff --git a/chef/cookbooks/windows/metadata.rb b/chef/cookbooks/windows/metadata.rb new file mode 100644 index 0000000..4dc43af --- /dev/null +++ b/chef/cookbooks/windows/metadata.rb @@ -0,0 +1,9 @@ +name "windows" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Provides a set of useful Windows-specific primitives." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.10.1" +supports "windows" +depends "chef_handler" diff --git a/chef/cookbooks/windows/providers/auto_run.rb b/chef/cookbooks/windows/providers/auto_run.rb new file mode 100644 index 0000000..36d84d6 --- /dev/null +++ b/chef/cookbooks/windows/providers/auto_run.rb @@ -0,0 +1,32 @@ +# +# Author:: Paul Morotn () +# Cookbook Name:: windows +# Provider:: auto_run +# +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :create do + windows_registry 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' do + values new_resource.name => "\"#{new_resource.program}\" #{new_resource.args}" + end +end + +action :remove do + windows_registry 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' do + values new_resource.name => '' + action :remove + end +end \ No newline at end of file diff --git a/chef/cookbooks/windows/providers/batch.rb b/chef/cookbooks/windows/providers/batch.rb new file mode 100644 index 0000000..9aa347c --- /dev/null +++ b/chef/cookbooks/windows/providers/batch.rb @@ -0,0 +1,62 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windws +# Provider:: batch +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'tempfile' +require 'chef/resource/execute' + +action :run do + begin + script_file.puts(@new_resource.code) + script_file.close + set_owner_and_group + + # cwd hax...shell_out on windows needs to support proper 'cwd' + # follow CHEF-2357 for more + cwd = @new_resource.cwd ? "cd \"#{@new_resource.cwd}\" & " : "" + + r = Chef::Resource::Execute.new(@new_resource.name, run_context) + r.user(@new_resource.user) + r.group(@new_resource.group) + r.command("#{cwd}call \"#{script_file.path}\" #{@new_resource.flags}") + r.creates(@new_resource.creates) + r.returns(@new_resource.returns) + r.run_action(:run) + + @new_resource.updated_by_last_action(r.updated_by_last_action?) + ensure + unlink_script_file + end +end + +private +def set_owner_and_group + # FileUtils itself implements a no-op if +user+ or +group+ are nil + # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file') + # as an unprivileged user. + FileUtils.chown(@new_resource.user, @new_resource.group, script_file.path) +end + +def script_file + @script_file ||= Tempfile.open(['chef-script', '.bat']) +end + +def unlink_script_file + @script_file && @script_file.close! +end diff --git a/chef/cookbooks/windows/providers/feature_dism.rb b/chef/cookbooks/windows/providers/feature_dism.rb new file mode 100644 index 0000000..5db9d63 --- /dev/null +++ b/chef/cookbooks/windows/providers/feature_dism.rb @@ -0,0 +1,49 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Provider:: feature_dism +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Chef::Provider::WindowsFeature::Base +include Chef::Mixin::ShellOut +include Windows::Helper + +def install_feature(name) + # return code 3010 is valid, it indicates a reboot is required + shell_out!("#{dism} /online /enable-feature /featurename:#{@new_resource.feature_name} /norestart", {:returns => [0,42,127,3010]}) +end + +def remove_feature(name) + # return code 3010 is valid, it indicates a reboot is required + shell_out!("#{dism} /online /disable-feature /featurename:#{@new_resource.feature_name} /norestart", {:returns => [0,42,127,3010]}) +end + +def installed? + @installed ||= begin + cmd = shell_out("#{dism} /online /Get-Features", {:returns => [0,42,127]}) + cmd.stderr.empty? && (cmd.stdout =~ /^Feature Name : #{@new_resource.feature_name}.?$\n^State : Enabled.?$/i) + end +end + +private +# account for File System Redirector +# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx +def dism + @dism ||= begin + locate_sysnative_cmd("dism.exe") + end +end diff --git a/chef/cookbooks/windows/providers/feature_servermanagercmd.rb b/chef/cookbooks/windows/providers/feature_servermanagercmd.rb new file mode 100644 index 0000000..b43749b --- /dev/null +++ b/chef/cookbooks/windows/providers/feature_servermanagercmd.rb @@ -0,0 +1,47 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Provider:: feature_servermanagercmd +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Chef::Provider::WindowsFeature::Base +include Chef::Mixin::ShellOut +include Windows::Helper + +def install_feature(name) + shell_out!("#{servermanagercmd} -install #{@new_resource.feature_name}", {:returns => [0,42,127]}) +end + +def remove_feature(name) + shell_out!("#{servermanagercmd} -remove #{@new_resource.feature_name}", {:returns => [0,42,127]}) +end + +def installed? + @installed ||= begin + cmd = shell_out("#{servermanagercmd} -query", {:returns => [0,42,127]}) + cmd.stderr.empty? && (cmd.stdout =~ /^\s*?\[X\]\s.+?\s\[#{@new_resource.feature_name}\]$/i) + end +end + +private +# account for File System Redirector +# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx +def servermanagercmd + @servermanagercmd ||= begin + locate_sysnative_cmd("servermanagercmd.exe") + end +end diff --git a/chef/cookbooks/windows/providers/package.rb b/chef/cookbooks/windows/providers/package.rb new file mode 100644 index 0000000..01bc860 --- /dev/null +++ b/chef/cookbooks/windows/providers/package.rb @@ -0,0 +1,252 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Provider:: package +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + require 'win32/registry' +end + +require 'chef/mixin/shell_out' +require 'chef/mixin/language' + +include Chef::Mixin::ShellOut +include Windows::Helper + +# the logic in all action methods mirror that of +# the Chef::Provider::Package which will make +# refactoring into core chef easy + +action :install do + # If we specified a version, and it's not the current version, move to the specified version + if @new_resource.version != nil && @new_resource.version != @current_resource.version + install_version = @new_resource.version + # If it's not installed at all, install it + elsif @current_resource.version == nil + install_version = candidate_version + end + + if install_version + Chef::Log.info("Installing #{@new_resource} version #{install_version}") + status = install_package(@new_resource.package_name, install_version) + if status + @new_resource.updated_by_last_action(true) + end + end +end + +action :upgrade do + if @current_resource.version != candidate_version + orig_version = @current_resource.version || "uninstalled" + Chef::Log.info("Upgrading #{@new_resource} version from #{orig_version} to #{candidate_version}") + status = upgrade_package(@new_resource.package_name, candidate_version) + if status + @new_resource.updated_by_last_action(true) + end + end +end + +action :remove do + if removing_package? + Chef::Log.info("Removing #{@new_resource}") + remove_package(@current_resource.package_name, @new_resource.version) + @new_resource.updated_by_last_action(true) + else + end +end + +def removing_package? + if @current_resource.version.nil? + false # nothing to remove + elsif @new_resource.version.nil? + true # remove any version of a package + elsif @new_resource.version == @current_resource.version + true # remove the version we have + else + false # we don't have the version we want to remove + end +end + +def expand_options(options) + options ? " #{options}" : "" +end + +# these methods are the required overrides of +# a provider that extends from Chef::Provider::Package +# so refactoring into core Chef should be easy + +def load_current_resource + @current_resource = Chef::Resource::WindowsPackage.new(@new_resource.name) + @current_resource.package_name(@new_resource.package_name) + @current_resource.version(nil) + + unless current_installed_version.nil? + @current_resource.version(current_installed_version) + end + + @current_resource +end + +def current_installed_version + @current_installed_version ||= begin + if installed_packages.include?(@new_resource.package_name) + installed_packages[@new_resource.package_name][:version] + end + end +end + +def candidate_version + @candidate_version ||= begin + @new_resource.version || 'latest' + end +end + +def install_package(name,version) + Chef::Log.debug("Processing #{@new_resource} as a #{installer_type} installer.") + install_args = [cached_file(@new_resource.source, @new_resource.checksum), expand_options(unattended_installation_flags), expand_options(@new_resource.options)] + Chef::Log.info("Starting installation...this could take awhile.") + Chef::Log.debug "Install command: #{ sprintf(install_command_template, *install_args) }" + shell_out!(sprintf(install_command_template, *install_args), {:timeout => @new_resource.timeout, :returns => @new_resource.success_codes}) +end + +def remove_package(name, version) + uninstall_string = installed_packages[@new_resource.package_name][:uninstall_string] + Chef::Log.info("Registry provided uninstall string for #{@new_resource} is '#{uninstall_string}'") + uninstall_command = begin + if uninstall_string =~ /msiexec/i + "#{uninstall_string} /qn" + else + uninstall_string.gsub!('"','') + "start \"\" /wait /d\"#{::File.dirname(uninstall_string)}\" #{::File.basename(uninstall_string)}#{expand_options(@new_resource.options)} /S" + end + end + Chef::Log.info("Removing #{@new_resource} with uninstall command '#{uninstall_command}'") + shell_out!(uninstall_command, {:returns => @new_resource.success_codes}) +end + +private + +def install_command_template + case installer_type + when :msi + "msiexec%2$s \"%1$s\"%3$s" + else + "start \"\" /wait \"%1$s\"%2$s%3$s" + end +end + +def uninstall_command_template + case installer_type + when :msi + "msiexec %2$s %1$s" + else + "start \"\" /wait /d%1$s %2$s %3$s" + end +end + +# http://unattended.sourceforge.net/installers.php +def unattended_installation_flags + case installer_type + when :msi + # this is no-ui + "/qn /i" + when :installshield + "/s /sms" + when :nsis + "/S /NCRC" + when :inno + #"/sp- /silent /norestart" + "/verysilent /norestart" + when :wise + "/s" + else + end +end + +def installed_packages + @installed_packages || begin + installed_packages = {} + # Computer\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall + installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE)) #rescue nil + # 64-bit registry view + # Computer\HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall + installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0100))) #rescue nil + # 32-bit registry view + # Computer\HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall + installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0200))) #rescue nil + # Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall + installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_CURRENT_USER)) #rescue nil + installed_packages + end +end + +def extract_installed_packages_from_key(hkey = ::Win32::Registry::HKEY_LOCAL_MACHINE, desired = ::Win32::Registry::Constants::KEY_READ) + uninstall_subkey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall' + packages = {} + begin + ::Win32::Registry.open(hkey, uninstall_subkey, desired) do |reg| + reg.each_key do |key, wtime| + begin + k = reg.open(key, desired) + display_name = k["DisplayName"] rescue nil + version = k["DisplayVersion"] rescue "NO VERSION" + uninstall_string = k["UninstallString"] rescue nil + if display_name + packages[display_name] = {:name => display_name, + :version => version, + :uninstall_string => uninstall_string} + end + rescue ::Win32::Registry::Error + end + end + end + rescue ::Win32::Registry::Error + end + packages +end + +def installer_type + @installer_type || begin + if @new_resource.installer_type + @new_resource.installer_type + else + basename = ::File.basename(cached_file(@new_resource.source, @new_resource.checksum)) + if basename.split(".").last.downcase == "msi" # Microsoft MSI + :msi + else + # search the binary file for installer type + contents = ::Kernel.open(::File.expand_path(cached_file(@new_resource.source)), "rb") {|io| io.read } # TODO limit data read in + case contents + when /inno/i # Inno Setup + :inno + when /wise/i # Wise InstallMaster + :wise + when /nsis/i # Nullsoft Scriptable Install System + :nsis + else + # if file is named 'setup.exe' assume installshield + if basename == "setup.exe" + :installshield + else + raise Chef::Exceptions::AttributeNotFound, "installer_type could not be determined, please set manually" + end + end + end + end + end +end diff --git a/chef/cookbooks/windows/providers/pagefile.rb b/chef/cookbooks/windows/providers/pagefile.rb new file mode 100644 index 0000000..e80247e --- /dev/null +++ b/chef/cookbooks/windows/providers/pagefile.rb @@ -0,0 +1,153 @@ +# +# Author:: Kevin Moser () +# Cookbook Name:: windows +# Provider:: pagefile +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Chef::Mixin::ShellOut +include Windows::Helper + +action :set do + pagefile = @new_resource.name + initial_size = @new_resource.initial_size + maximum_size = @new_resource.maximum_size + system_managed = @new_resource.system_managed + automatic_managed = @new_resource.automatic_managed + updated = false + + if automatic_managed + unless automatic_managed? + set_automatic_managed + updated = true + end + else + if automatic_managed? + unset_automatic_managed + updated = true + end + + # Check that the resource is not just trying to unset automatic managed, if it is do nothing more + if (initial_size && maximum_size) || system_managed + unless exists?(pagefile) + create(pagefile) + end + + if system_managed + unless max_and_min_set?(pagefile, 0, 0) + set_system_managed(pagefile) + updated = true + end + else + unless max_and_min_set?(pagefile, initial_size, maximum_size) + set_custom_size(pagefile, initial_size, maximum_size) + updated = true + end + end + end + end + + @new_resource.updated_by_last_action(updated) +end + +action :delete do + pagefile = @new_resource.name + updated = false + + if exists?(pagefile) + delete(pagefile) + updated = true + end + + @new_resource.updated_by_last_action(updated) +end + + +private +def exists?(pagefile) + @exists ||= begin + cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", {:returns => [0]}) + cmd.stderr.empty? && (cmd.stdout =~ /SettingID=#{get_setting_id(pagefile)}/i) + end +end + +def max_and_min_set?(pagefile, min, max) + @max_and_min_set ||= begin + cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", {:returns => [0]}) + cmd.stderr.empty? && (cmd.stdout =~ /InitialSize=#{min}/i) && (cmd.stdout =~ /MaximumSize=#{max}/i) + end +end + +def create(pagefile) + Chef::Log.debug("Creating pagefile #{pagefile}") + cmd = shell_out("#{wmic} pagefileset create name=\"#{win_friendly_path(pagefile)}\"") + check_for_errors(cmd.stderr) +end + +def delete(pagefile) + Chef::Log.debug("Removing pagefile #{pagefile}") + cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" delete") + check_for_errors(cmd.stderr) +end + +def automatic_managed? + @automatic_managed ||= begin + cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" get AutomaticManagedPagefile /format:list") + cmd.stderr.empty? && (cmd.stdout =~ /AutomaticManagedPagefile=TRUE/i) + end +end + +def set_automatic_managed + Chef::Log.debug("Setting pagefile to Automatic Managed") + cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=True") + check_for_errors(cmd.stderr) +end + +def unset_automatic_managed + Chef::Log.debug("Setting pagefile to User Managed") + cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False") + check_for_errors(cmd.stderr) +end + +def set_custom_size(pagefile, min, max) + Chef::Log.debug("Setting #{pagefile} to InitialSize=#{min} & MaximumSize=#{max}") + cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=#{min},MaximumSize=#{max}", {:returns => [0]}) + check_for_errors(cmd.stderr) +end + +def set_system_managed(pagefile) + Chef::Log.debug("Setting #{pagefile} to System Managed") + cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=0,MaximumSize=0", {:returns => [0]}) + check_for_errors(cmd.stderr) +end + +def get_setting_id(pagefile) + pagefile = win_friendly_path(pagefile) + pagefile = pagefile.split("\\") + "#{pagefile[1]} @ #{pagefile[0]}" +end + +def check_for_errors(stderr) + unless stderr.empty? + Chef::Log.fatal(stderr) + end +end + +def wmic + @wmic ||= begin + locate_sysnative_cmd("wmic.exe") + end +end \ No newline at end of file diff --git a/chef/cookbooks/windows/providers/path.rb b/chef/cookbooks/windows/providers/path.rb new file mode 100644 index 0000000..6ec9191 --- /dev/null +++ b/chef/cookbooks/windows/providers/path.rb @@ -0,0 +1,35 @@ +# +# Author:: Paul Morotn () +# Cookbook Name:: windows +# Provider:: path +# +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :add do + env "PATH" do + action :modify + delim ::File::PATH_SEPARATOR + value new_resource.path + end +end + +action :remove do + env "PATH" do + action :delete + delim ::File::PATH_SEPARATOR + value new_resource.path + end +end \ No newline at end of file diff --git a/chef/cookbooks/windows/providers/printer.rb b/chef/cookbooks/windows/providers/printer.rb new file mode 100644 index 0000000..d53e287 --- /dev/null +++ b/chef/cookbooks/windows/providers/printer.rb @@ -0,0 +1,100 @@ +# +# Author:: Doug Ireton () +# Cookbook Name:: windows +# Provider:: printer +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Support whyrun +def whyrun_supported? + true +end + +action :create do + if @current_resource.exists + Chef::Log.info "#{ @new_resource } already exists - nothing to do." + else + converge_by("Create #{ @new_resource }") do + create_printer + end + end +end + +action :delete do + if @current_resource.exists + converge_by("Delete #{ @new_resource }") do + delete_printer + end + else + Chef::Log.info "#{ @current_resource } doesn't exist - can't delete." + end +end + +def load_current_resource + @current_resource = Chef::Resource::WindowsPrinter.new(@new_resource.name) + @current_resource.name(@new_resource.name) + + if printer_exists?(@current_resource.name) + # TODO: Set @current_resource printer properties from registry + @current_resource.exists = true + end +end + + +private + +PRINTERS_REG_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'.freeze unless defined?(PRINTERS_REG_KEY) + +def printer_exists?(name) + printer_reg_key = PRINTERS_REG_KEY + name + Chef::Log.debug "Checking to see if this reg key exists: '#{ printer_reg_key }'" + Registry.key_exists?(printer_reg_key) +end + +def create_printer + + # Create the printer port first + windows_printer_port new_resource.ipv4_address do + end + + port_name = "IP_#{ new_resource.ipv4_address }" + + powershell "Creating printer: #{ new_resource.name }" do + code <<-EOH + + Set-WmiInstance -class Win32_Printer ` + -EnableAllPrivileges ` + -Argument @{ DeviceID = "#{ new_resource.device_id }"; + Comment = "#{ new_resource.comment }"; + Default = "$#{ new_resource.default }"; + DriverName = "#{ new_resource.driver_name }"; + Location = "#{ new_resource.location }"; + PortName = "#{ port_name }"; + Shared = "$#{ new_resource.shared }"; + ShareName = "#{ new_resource.share_name }"; + } + EOH + end +end + +def delete_printer + powershell "Deleting printer: #{ new_resource.name }" do + code <<-EOH + $printer = Get-WMIObject -class Win32_Printer -EnableAllPrivileges -Filter "name = '#{ new_resource.name }'" + $printer.Delete() + EOH + end +end diff --git a/chef/cookbooks/windows/providers/printer_port.rb b/chef/cookbooks/windows/providers/printer_port.rb new file mode 100644 index 0000000..ab88247 --- /dev/null +++ b/chef/cookbooks/windows/providers/printer_port.rb @@ -0,0 +1,102 @@ +# +# Author:: Doug Ireton () +# Cookbook Name:: windows +# Provider:: printer_port +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Support whyrun +def whyrun_supported? + true +end + +action :create do + if @current_resource.exists + Chef::Log.info "#{ @new_resource } already exists - nothing to do." + else + converge_by("Create #{ @new_resource }") do + create_printer_port + end + end +end + +action :delete do + if @current_resource.exists + converge_by("Delete #{ @new_resource }") do + delete_printer_port + end + else + Chef::Log.info "#{ @current_resource } doesn't exist - can't delete." + end +end + +def load_current_resource + @current_resource = Chef::Resource::WindowsPrinterPort.new(@new_resource.name) + @current_resource.name(@new_resource.name) + @current_resource.ipv4_address(@new_resource.ipv4_address) + @current_resource.port_name(@new_resource.port_name || "IP_#{ @new_resource.ipv4_address }") + + if port_exists?(@current_resource.port_name) + # TODO: Set @current_resource port properties from registry + @current_resource.exists = true + end +end + + +private + +PORTS_REG_KEY = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\\'.freeze unless defined?(PORTS_REG_KEY) + +def port_exists?(name) + port_reg_key = PORTS_REG_KEY + name + + Chef::Log.debug "Checking to see if this reg key exists: '#{ port_reg_key }'" + Registry.key_exists?(port_reg_key) +end + + +def create_printer_port + + port_name = new_resource.port_name || "IP_#{ new_resource.ipv4_address }" + + # create the printer port using PowerShell + powershell "Creating printer port #{ new_resource.port_name }" do + code <<-EOH + + Set-WmiInstance -class Win32_TCPIPPrinterPort ` + -EnableAllPrivileges ` + -Argument @{ HostAddress = "#{ new_resource.ipv4_address }"; + Name = "#{ port_name }"; + Description = "#{ new_resource.port_description }"; + PortNumber = "#{ new_resource.port_number }"; + Protocol = "#{ new_resource.port_protocol }"; + SNMPEnabled = "$#{ new_resource.snmp_enabled }"; + } + EOH + end +end + +def delete_printer_port + + port_name = new_resource.port_name || "IP_#{ new_resource.ipv4_address }" + + powershell "Deleting printer port: #{ new_resource.port_name }" do + code <<-EOH + $port = Get-WMIObject -class Win32_TCPIPPrinterPort -EnableAllPrivileges -Filter "name = '#{ port_name }'" + $port.Delete() + EOH + end +end diff --git a/chef/cookbooks/windows/providers/reboot.rb b/chef/cookbooks/windows/providers/reboot.rb new file mode 100644 index 0000000..4fc5032 --- /dev/null +++ b/chef/cookbooks/windows/providers/reboot.rb @@ -0,0 +1,31 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Provider:: reboot +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :request do + node.run_state[:reboot_requested] = true + node.run_state[:reboot_timeout] = @new_resource.timeout + node.run_state[:reboot_reason] = @new_resource.reason +end + +action :cancel do + node.run_state.delete(:reboot_requested) + node.run_state.delete(:reboot_timeout) + node.run_state.delete(:reboot_reason) +end diff --git a/chef/cookbooks/windows/providers/registry.rb b/chef/cookbooks/windows/providers/registry.rb new file mode 100644 index 0000000..afd03ff --- /dev/null +++ b/chef/cookbooks/windows/providers/registry.rb @@ -0,0 +1,72 @@ +# +# Author:: Doug MacEachern () +# Author:: Seth Chisamore () +# Author:: Paul Morton () +# Cookbook Name:: windows +# Provider:: registry +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Opscode, Inc. +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Windows::RegistryHelper + +action :create do + registry_update(:create) +end + +action :modify do + registry_update(:open) +end + +action :force_modify do + require 'timeout' + Timeout.timeout(120) do + @new_resource.values.each do |value_name, value_data| + i = 1 + until i > 5 do + desired_value_data = value_data + current_value_data = get_value(@new_resource.key_name.dup, value_name.dup) + if current_value_data.to_s == desired_value_data.to_s + Chef::Log.debug("#{@new_resource} value [#{value_name}] desired [#{desired_value_data}] data already set. Check #{i}/5.") + i+=1 + else + Chef::Log.debug("#{@new_resource} value [#{value_name}] current [#{current_value_data}] data not equal to desired [#{desired_value_data}] data. Setting value and restarting check loop.") + begin + registry_update(:open) + rescue Exception + registry_update(:create) + end + i=0 # start count loop over + end + end + end + break + end +end + +action :remove do + delete_value(@new_resource.key_name,@new_resource.values) +end + +private +def registry_update(mode) + + Chef::Log.debug("Registry Mode (#{mode})") + updated = set_value(mode,@new_resource.key_name,@new_resource.values,@new_resource.type) + @new_resource.updated_by_last_action(updated) + +end diff --git a/chef/cookbooks/windows/providers/shortcut.rb b/chef/cookbooks/windows/providers/shortcut.rb new file mode 100644 index 0000000..9fd9a88 --- /dev/null +++ b/chef/cookbooks/windows/providers/shortcut.rb @@ -0,0 +1,56 @@ +# +# Author:: Doug MacEachern +# Cookbook Name:: windows +# Provider:: shortcut +# +# Copyright:: 2010, VMware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def load_current_resource + require 'win32ole' + + @link = WIN32OLE.new("WScript.Shell").CreateShortcut(@new_resource.name) + + @current_resource = Chef::Resource::WindowsShortcut.new(@new_resource.name) + @current_resource.name(@new_resource.name) + @current_resource.target(@link.TargetPath) + @current_resource.arguments(@link.Arguments) + @current_resource.description(@link.Description) + @current_resource.cwd(@link.WorkingDirectory) +end + +# Check to see if the shorcut needs any changes +# +# === Returns +# :: If a change is required +# :: If the shorcuts are identical +def compare_shortcut + [:target, :arguments, :description, :cwd].any? do |attr| + !@new_resource.send(attr).nil? && @current_resource.send(attr) != @new_resource.send(attr) + end +end + +def action_create + if compare_shortcut + @link.TargetPath = @new_resource.target if @new_resource.target != nil + @link.Arguments = @new_resource.arguments if @new_resource.arguments != nil + @link.Description = @new_resource.description if @new_resource.description != nil + @link.WorkingDirectory = @new_resource.cwd if @new_resource.cwd != nil + #ignoring: WindowStyle, Hotkey, IconLocation + @link.Save + Chef::Log.info("Added #{@new_resource} shortcut") + new_resource.updated_by_last_action(true) + end +end diff --git a/chef/cookbooks/windows/providers/task.rb b/chef/cookbooks/windows/providers/task.rb new file mode 100644 index 0000000..fbe8bd2 --- /dev/null +++ b/chef/cookbooks/windows/providers/task.rb @@ -0,0 +1,127 @@ +# +# Author:: Paul Mooring () +# Cookbook Name:: windows +# Provider:: task +# +# Copyright:: 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' +include Chef::Mixin::ShellOut + +action :create do + if @current_resource.exists + Chef::Log.info "#{@new_resource} task already exists - nothing to do" + else + use_force = @new_resource.force ? '/F' : '' + cmd = "schtasks /Create #{use_force} /TN \"#{@new_resource.name}\" " + cmd += "/SC #{@new_resource.frequency} " + cmd += "/MO #{@new_resource.frequency_modifier} " if [:minute, :hourly, :daily, :weekly, :monthly].include?(@new_resource.frequency) + cmd += "/TR \"#{@new_resource.command}\" " + if @new_resource.user && @new_resource.password + cmd += "/RU \"#{@new_resource.user}\" /RP \"#{@new_resource.password}\" " + elsif (@new_resource.user and !@new_resource.password) || (@new_resource.password and !@new_resource.user) + Chef::Log.fatal "#{@new_resource.name}: Can't specify user or password without both!" + end + cmd += "/RL HIGHEST " if @new_resource.run_level == :highest + shell_out!(cmd, {:returns => [0]}) + @new_resource.updated_by_last_action true + Chef::Log.info "#{@new_resource} task created" + end +end + +action :run do + if @current_resource.exists + if @current_resource.status == :running + Chef::Log.info "#{@new_resource} task is currently running, skipping run" + else + cmd = "schtasks /Run /TN \"#{@current_resource.name}\"" + shell_out!(cmd, {:returns => [0]}) + @new_resource.updated_by_last_action true + Chef::Log.info "#{@new_resource} task ran" + end + else + Chef::Log.debug "#{@new_resource} task doesn't exists - nothing to do" + end +end + +action :change do + if @current_resource.exists + cmd = "schtasks /Change /TN \"#{@current_resource.name}\" " + cmd += "/TR \"#{@new_resource.command}\" " if @new_resource.command + if @new_resource.user && @new_resource.password + cmd += "/RU \"#{@new_resource.user}\" /RP \"#{@new_resource.password}\" " + elsif (@new_resource.user and !@new_resource.password) || (@new_resource.password and !@new_resource.user) + Chef::Log.fatal "#{@new_resource.name}: Can't specify user or password without both!" + end + shell_out!(cmd, {:returns => [0]}) + @new_resource.updated_by_last_action true + Chef::Log.info "Change #{@new_resource} task ran" + else + Chef::Log.debug "#{@new_resource} task doesn't exists - nothing to do" + end +end + +action :delete do + if @current_resource.exists + use_force = @new_resource.force ? '/F' : '' + cmd = "schtasks /Delete #{use_force} /TN \"#{@current_resource.name}\"" + shell_out!(cmd, {:returns => [0]}) + @new_resource.updated_by_last_action true + Chef::Log.info "#{@new_resource} task deleted" + else + Chef::Log.debug "#{@new_resource} task doesn't exists - nothing to do" + end +end + +def load_current_resource + @current_resource = Chef::Resource::WindowsTask.new(@new_resource.name) + @current_resource.name(@new_resource.name) + + task_hash = load_task_hash(@current_resource.name) + if task_hash[:TaskName] == '\\' + @new_resource.name + @current_resource.exists = true + if task_hash[:Status] == "Running" + @current_resource.status = :running + end + @current_resource.cwd(task_hash[:Folder]) + @current_resource.command(task_hash[:TaskToRun]) + @current_resource.user(task_hash[:RunAsUser]) + end if task_hash.respond_to? :[] +end + +private + +def load_task_hash(task_name) + Chef::Log.debug "looking for existing tasks" + output = `schtasks /Query /FO LIST /V /TN \"#{task_name}\" 2> NUL` + if output.empty? + task = false + else + task = Hash.new + + output.split("\n").map! do |line| + line.split(":", 2).map! do |field| + field.strip + end + end.each do |field| + if field.kind_of? Array and field[0].respond_to? :to_sym + task[field[0].gsub(/\s+/,"").to_sym] = field[1] + end + end + end + + task +end diff --git a/chef/cookbooks/windows/providers/zipfile.rb b/chef/cookbooks/windows/providers/zipfile.rb new file mode 100644 index 0000000..44b3731 --- /dev/null +++ b/chef/cookbooks/windows/providers/zipfile.rb @@ -0,0 +1,91 @@ +# +# Author:: Doug MacEachern () +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Provider:: unzip +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Windows::Helper + +require 'find' + +action :unzip do + ensure_rubyzip_gem_installed + Chef::Log.debug("unzip #{@new_resource.source} => #{@new_resource.path} (overwrite=#{@new_resource.overwrite})") + + Zip::ZipFile.open(cached_file(@new_resource.source, @new_resource.checksum)) do |zip| + zip.each do |entry| + path = ::File.join(@new_resource.path, entry.name) + FileUtils.mkdir_p(::File.dirname(path)) + if @new_resource.overwrite && ::File.exists?(path) && !::File.directory?(path) + FileUtils.rm(path) + end + zip.extract(entry, path) + end + end + @new_resource.updated_by_last_action(true) +end + +action :zip do + ensure_rubyzip_gem_installed + # sanitize paths for windows. + @new_resource.source.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR) + @new_resource.path.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR) + Chef::Log.debug("zip #{@new_resource.source} => #{@new_resource.path} (overwrite=#{@new_resource.overwrite})") + + if @new_resource.overwrite == false && ::File.exists?(@new_resource.path) + Chef::Log.info("file #{@new_resource.path} already exists and overwrite is set to false, exiting") + else + # delete the archive if it already exists, because we are recreating it. + if ::File.exists?(@new_resource.path) + ::File.unlink(@new_resource.path) + end + # only supporting compression of a single directory (recursively). + if ::File.directory?(@new_resource.source) + z = Zip::ZipFile.new(@new_resource.path, true) + unless @new_resource.source =~ /::File::ALT_SEPARATOR$/ + @new_resource.source << ::File::ALT_SEPARATOR + end + Find.find(@new_resource.source) do |f| + f.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR) + # don't add root directory to the zipfile. + next if f == @new_resource.source + # strip the root directory from the filename before adding it to the zipfile. + zip_fname = f.sub(@new_resource.source, '') + Chef::Log.debug("adding #{zip_fname} to archive, sourcefile is: #{f}") + z.add(zip_fname, f) + end + z.close + else + Chef::Log.info("Single directory must be specified for compression, and #{@new_resource.source} does not meet that criteria.") + end + end +end + +private +def ensure_rubyzip_gem_installed + begin + require 'zip/zip' + rescue LoadError + Chef::Log.info("Missing gem 'rubyzip'...installing now.") + chef_gem "rubyzip" do + version node['windows']['rubyzipversion'] + end + require 'zip/zip' + end +end diff --git a/chef/cookbooks/windows/recipes/default.rb b/chef/cookbooks/windows/recipes/default.rb new file mode 100644 index 0000000..f0dbffb --- /dev/null +++ b/chef/cookbooks/windows/recipes/default.rb @@ -0,0 +1,34 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Recipe:: default +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# gems with precompiled binaries +%w{ win32-api win32-service }.each do |win_gem| + chef_gem win_gem do + options '--platform=mswin32' + action :install + end +end + +# the rest +%w{ windows-api windows-pr win32-dir win32-event win32-mutex }.each do |win_gem| + chef_gem win_gem do + action :install + end +end diff --git a/chef/cookbooks/windows/recipes/reboot_handler.rb b/chef/cookbooks/windows/recipes/reboot_handler.rb new file mode 100644 index 0000000..2e55b91 --- /dev/null +++ b/chef/cookbooks/windows/recipes/reboot_handler.rb @@ -0,0 +1,32 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Recipe:: restart_handler +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +remote_directory node['chef_handler']['handler_path'] do + source 'handlers' + recursive true + action :create +end + +chef_handler 'WindowsRebootHandler' do + source "#{node['chef_handler']['handler_path']}/windows_reboot_handler.rb" + arguments node['windows']['allow_pending_reboots'] + supports :report => true, :exception => false + action :enable +end diff --git a/chef/cookbooks/windows/resources/auto_run.rb b/chef/cookbooks/windows/resources/auto_run.rb new file mode 100644 index 0000000..7beecc5 --- /dev/null +++ b/chef/cookbooks/windows/resources/auto_run.rb @@ -0,0 +1,30 @@ +# +# Author:: Paul Morotn () +# Cookbook Name:: windows +# Resource:: auto_run +# +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def initialize(name,run_context=nil) + super + @action = :create +end + +actions :create, :remove + +attribute :program, :kind_of => String +attribute :name, :kind_of => String, :name_attribute => true +attribute :args, :kind_of => String, :default => '' diff --git a/chef/cookbooks/windows/resources/batch.rb b/chef/cookbooks/windows/resources/batch.rb new file mode 100644 index 0000000..7d4e917 --- /dev/null +++ b/chef/cookbooks/windows/resources/batch.rb @@ -0,0 +1,36 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: batch +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :run + +attribute :command, :kind_of => String, :name_attribute => true +attribute :cwd, :kind_of => String, :default => nil +attribute :code, :kind_of => String, :default => nil +attribute :user, :kind_of => [ String, Integer ], :default => nil +attribute :group, :kind_of => [ String, Integer ], :default => nil +attribute :creates, :kind_of => [ String ], :default => nil +attribute :flags, :kind_of => [ String ], :default => nil +attribute :returns, :kind_of => [Integer, Array], :default => 0 + +def initialize(name, run_context=nil) + super + @action = :run + @command = name +end diff --git a/chef/cookbooks/windows/resources/feature.rb b/chef/cookbooks/windows/resources/feature.rb new file mode 100644 index 0000000..b67c0fb --- /dev/null +++ b/chef/cookbooks/windows/resources/feature.rb @@ -0,0 +1,40 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: feature +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Windows::Helper + +actions :install, :remove + +attribute :feature_name, :kind_of => String, :name_attribute => true + +def initialize(name, run_context=nil) + super + @action = :install + @provider = lookup_provider_constant(locate_default_provider) +end + +private +def locate_default_provider + if ::File.exists?(locate_sysnative_cmd('dism.exe')) + :windows_feature_dism + elsif ::File.exists?(locate_sysnative_cmd('servermanagercmd.exe')) + :windows_feature_servermanagercmd + end +end \ No newline at end of file diff --git a/chef/cookbooks/windows/resources/package.rb b/chef/cookbooks/windows/resources/package.rb new file mode 100644 index 0000000..a9e822e --- /dev/null +++ b/chef/cookbooks/windows/resources/package.rb @@ -0,0 +1,46 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: package +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :install, :remove + +default_action :install + +attribute :package_name, :kind_of => String, :name_attribute => true +attribute :source, :kind_of => String, :required => true +attribute :version, :kind_of => String +attribute :options, :kind_of => String +attribute :installer_type, :kind_of => Symbol, :default => nil, :equal_to => [:msi, :inno, :nsis, :wise, :installshield, :custom] +attribute :checksum, :kind_of => String +attribute :timeout, :kind_of => Integer, :default => 600 +attribute :success_codes, :kind_of => Array, :default => [0, 42, 127] + +# TODO + +# add preseeding support +#attribute :response_file + +# allow target dirtory of installation to be set +#attribute :target_dir + +# Covers 0.10.8 and earlier +def initialize(*args) + super + @action = :install +end diff --git a/chef/cookbooks/windows/resources/pagefile.rb b/chef/cookbooks/windows/resources/pagefile.rb new file mode 100644 index 0000000..3d95f13 --- /dev/null +++ b/chef/cookbooks/windows/resources/pagefile.rb @@ -0,0 +1,29 @@ +# +# Author:: Kevin Moser () +# Cookbook Name:: windows +# Resource:: pagefile +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :set, :delete + +attribute :name, :kind_of => String, :name_attribute => true +attribute :system_managed, :kind_of => [TrueClass, FalseClass] +attribute :automatic_managed, :kind_of => [TrueClass, FalseClass], :default => false +attribute :initial_size, :kind_of => Integer +attribute :maximum_size, :kind_of => Integer + +default_action :set \ No newline at end of file diff --git a/chef/cookbooks/windows/resources/path.rb b/chef/cookbooks/windows/resources/path.rb new file mode 100644 index 0000000..f39aa8e --- /dev/null +++ b/chef/cookbooks/windows/resources/path.rb @@ -0,0 +1,28 @@ +# +# Author:: Paul Morotn () +# Cookbook Name:: windows +# Resource:: path +# +# Copyright:: 2011, Business Intelligence Associates, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def initialize(name,run_context=nil) + super + @action = :add +end + +actions :add, :remove + +attribute :path, :kind_of => String, :name_attribute => true diff --git a/chef/cookbooks/windows/resources/printer.rb b/chef/cookbooks/windows/resources/printer.rb new file mode 100644 index 0000000..5effa33 --- /dev/null +++ b/chef/cookbooks/windows/resources/printer.rb @@ -0,0 +1,41 @@ +# +# Author:: Doug Ireton () +# Cookbook Name:: windows +# Resource:: printer +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# See here for more info: +# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx + +require 'resolv' + +actions :create, :delete + +default_action :create + +attribute :device_id, :kind_of => String, :name_attribute => true, + :required => true +attribute :comment, :kind_of => String + +attribute :default, :kind_of => [ TrueClass, FalseClass ], :default => false +attribute :driver_name, :kind_of => String, :required => true +attribute :location, :kind_of => String +attribute :shared, :kind_of => [ TrueClass, FalseClass ], :default => false +attribute :share_name, :kind_of => String + +attribute :ipv4_address, :kind_of => String, :regex => Resolv::IPv4::Regex + +attr_accessor :exists diff --git a/chef/cookbooks/windows/resources/printer_port.rb b/chef/cookbooks/windows/resources/printer_port.rb new file mode 100644 index 0000000..b79a6fc --- /dev/null +++ b/chef/cookbooks/windows/resources/printer_port.rb @@ -0,0 +1,40 @@ +# +# Author:: Doug Ireton () +# Cookbook Name:: windows +# Resource:: printer_port +# +# Copyright:: 2012, Nordstrom, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# See here for more info: +# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx + +require 'resolv' + +actions :create, :delete + +default_action :create + +attribute :ipv4_address, :name_attribute => true, :kind_of => String, + :required => true, :regex => Resolv::IPv4::Regex + +attribute :port_name , :kind_of => String +attribute :port_number , :kind_of => Fixnum, :default => 9100 +attribute :port_description, :kind_of => String +attribute :snmp_enabled , :kind_of => [ TrueClass, FalseClass ], + :default => false + +attribute :port_protocol, :kind_of => Fixnum, :default => 1, :equal_to => [1, 2] + +attr_accessor :exists diff --git a/chef/cookbooks/windows/resources/reboot.rb b/chef/cookbooks/windows/resources/reboot.rb new file mode 100644 index 0000000..f19a3d2 --- /dev/null +++ b/chef/cookbooks/windows/resources/reboot.rb @@ -0,0 +1,29 @@ +# +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: reboot +# +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :request, :cancel + +attribute :timeout, :kind_of => Integer, :name_attribute => true +attribute :reason, :kind_of => String, :default => '' + +def initialize(name,run_context=nil) + super + @action = :request +end diff --git a/chef/cookbooks/windows/resources/registry.rb b/chef/cookbooks/windows/resources/registry.rb new file mode 100644 index 0000000..1289dbf --- /dev/null +++ b/chef/cookbooks/windows/resources/registry.rb @@ -0,0 +1,33 @@ +# +# Author:: Doug MacEachern () +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: registry +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :create, :modify, :force_modify, :remove + +attribute :key_name, :kind_of => String, :name_attribute => true +attribute :values, :kind_of => Hash +attribute :type, :kind_of => Symbol, :default => nil, :equal_to => [:binary, :string, :multi_string, :expand_string, :dword, :dword_big_endian, :qword] + +def initialize(name, run_context=nil) + super + @action = :modify + @key_name = name +end diff --git a/chef/cookbooks/windows/resources/shortcut.rb b/chef/cookbooks/windows/resources/shortcut.rb new file mode 100644 index 0000000..eb6268b --- /dev/null +++ b/chef/cookbooks/windows/resources/shortcut.rb @@ -0,0 +1,35 @@ +# +# Author:: Doug MacEachern +# Cookbook Name:: windows +# Resource:: shortcut +# +# Copyright:: 2010, VMware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :create + +default_action :create + +attribute :name, :kind_of => String +attribute :target, :kind_of => String +attribute :arguments, :kind_of => String +attribute :description, :kind_of => String +attribute :cwd, :kind_of => String + +# Covers 0.10.8 and earlier +def initialize(*args) + super + @action = :create +end diff --git a/chef/cookbooks/windows/resources/task.rb b/chef/cookbooks/windows/resources/task.rb new file mode 100644 index 0000000..3a696f5 --- /dev/null +++ b/chef/cookbooks/windows/resources/task.rb @@ -0,0 +1,48 @@ +# +# Author:: Paul Mooring () +# Cookbook Name:: windows +# Resource:: task +# +# Copyright:: 2012, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Passwords can't be loaded for existing tasks, making :modify both confusing +# and not very useful +actions :create, :delete, :run, :change + +attribute :name, :kind_of => String, :name_attribute => true +attribute :command, :kind_of => String +attribute :cwd, :kind_of => String +attribute :user, :kind_of => String, :default => nil +attribute :password, :kind_of => String, :default => nil +attribute :run_level, :equal_to => [:highest, :limited], :default => :limited +attribute :force, :kind_of => [ TrueClass, FalseClass ], :default => false +attribute :frequency_modifier, :kind_of => Integer, :default => 1 +attribute :frequency, :equal_to => [:minute, + :hourly, + :daily, + :weekly, + :monthly, + :once, + :on_logon, + :onstart, + :on_idle], :default => :hourly + +attr_accessor :exists, :status + +def initialize(name, run_context=nil) + super + @action = :create +end diff --git a/chef/cookbooks/windows/resources/zipfile.rb b/chef/cookbooks/windows/resources/zipfile.rb new file mode 100644 index 0000000..0265816 --- /dev/null +++ b/chef/cookbooks/windows/resources/zipfile.rb @@ -0,0 +1,33 @@ +# +# Author:: Doug MacEachern () +# Author:: Seth Chisamore () +# Cookbook Name:: windows +# Resource:: unzip +# +# Copyright:: 2010, VMware, Inc. +# Copyright:: 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :unzip, :zip + +attribute :path, :kind_of => String, :name_attribute => true +attribute :source, :kind_of => String +attribute :overwrite, :kind_of => [ TrueClass, FalseClass ], :default => false +attribute :checksum, :kind_of => String + +def initialize(name, run_context=nil) + super + @action = :unzip +end diff --git a/chef/cookbooks/xfs/.kitchen.yml b/chef/cookbooks/xfs/.kitchen.yml new file mode 100644 index 0000000..4a6ae2e --- /dev/null +++ b/chef/cookbooks/xfs/.kitchen.yml @@ -0,0 +1,34 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: ubuntu-12.04 + driver_config: + box: opscode-ubuntu-12.04 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box + run_list: + - recipe[apt] + +- name: ubuntu-10.04 + driver_config: + box: opscode-ubuntu-10.04 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_provisionerless.box + run_list: + - recipe[apt] + +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: http://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: default + run_list: ["recipe[xfs]"] + attributes: {} diff --git a/chef/cookbooks/xfs/Berksfile b/chef/cookbooks/xfs/Berksfile new file mode 100644 index 0000000..f08b074 --- /dev/null +++ b/chef/cookbooks/xfs/Berksfile @@ -0,0 +1,7 @@ +site :opscode + +metadata + +group :integration do + cookbook "apt" +end diff --git a/chef/cookbooks/xfs/CHANGELOG.md b/chef/cookbooks/xfs/CHANGELOG.md new file mode 100644 index 0000000..f433e24 --- /dev/null +++ b/chef/cookbooks/xfs/CHANGELOG.md @@ -0,0 +1,3 @@ +## v1.1.0: + +* [COOK-2076] - Add Amazon Linux support diff --git a/chef/cookbooks/xfs/CONTRIBUTING b/chef/cookbooks/xfs/CONTRIBUTING new file mode 100644 index 0000000..89ac873 --- /dev/null +++ b/chef/cookbooks/xfs/CONTRIBUTING @@ -0,0 +1,29 @@ +If you would like to contribute, please open a ticket in JIRA: + +* http://tickets.opscode.com + +Create the ticket in the COOK project and use the cookbook name as the +component. + +For all code contributions, we ask that contributors sign a +contributor license agreement (CLA). Instructions may be found here: + +* http://wiki.opscode.com/display/chef/How+to+Contribute + +When contributing changes to individual cookbooks, please do not +modify the version number in the metadata.rb. Also please do not +update the CHANGELOG.md for a new version. Not all changes to a +cookbook may be merged and released in the same versions. Opscode will +handle the version updates during the release process. You are welcome +to correct typos or otherwise make updates to documentation in the +README. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] Updated pool resource to correctly delete.' + +In the ticket itself, it is also helpful if you include log output of +a successful Chef run, but this is not absolutely required. diff --git a/chef/cookbooks/xfs/LICENSE b/chef/cookbooks/xfs/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/xfs/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/xfs/README.md b/chef/cookbooks/xfs/README.md new file mode 100644 index 0000000..3358ad8 --- /dev/null +++ b/chef/cookbooks/xfs/README.md @@ -0,0 +1,30 @@ +Description +==== + +Installs packages for working with XFS filesystems. + +Requirements +==== + +Tested on Ubuntu 10.04, CentOS 5.5, Amazon Linux and Scientific Linux 6.0. + +Should work on any Debian or Red Hat family Linux distributions that +have the xfs packages in a default repository. + +License and Author +==== + +Author:: Joshua Timberman () +Copyright:: 2009-2011, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/xfs/TESTING.md b/chef/cookbooks/xfs/TESTING.md new file mode 100644 index 0000000..e29ff7c --- /dev/null +++ b/chef/cookbooks/xfs/TESTING.md @@ -0,0 +1,25 @@ +This cookbook includes support for running tests via Test Kitchen (1.0). This has some requirements. + +1. You must be using the Git repository, rather than the downloaded cookbook from the Chef Community Site. +2. You must have Vagrant 1.1 installed. +3. You must have a "sane" Ruby 1.9.3 environment. + +Once the above requirements are met, install the additional requirements: + +Install the berkshelf plugin for vagrant, and berkshelf to your local Ruby environment. + + vagrant plugin install vagrant-berkshelf + gem install berkshelf + +Install Test Kitchen 1.0 (unreleased yet, use the alpha / prerelease version). + + gem install test-kitchen --pre + +Install the Vagrant driver for Test Kitchen. + + gem install kitchen-vagrant + +Once the above are installed, you should be able to run Test Kitchen: + + kitchen list + kitchen test diff --git a/chef/cookbooks/xfs/metadata.rb b/chef/cookbooks/xfs/metadata.rb new file mode 100644 index 0000000..8d838bb --- /dev/null +++ b/chef/cookbooks/xfs/metadata.rb @@ -0,0 +1,13 @@ +name "xfs" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs packages for working with XFS" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.0" + +recipe "xfs", "Installs packages for working with XFS" + +%w{ amazon debian ubuntu redhat centos scientific fedora }.each do |os| + supports os +end diff --git a/chef/cookbooks/xfs/recipes/default.rb b/chef/cookbooks/xfs/recipes/default.rb new file mode 100644 index 0000000..d738282 --- /dev/null +++ b/chef/cookbooks/xfs/recipes/default.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: xfs +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "xfsprogs" + +case node["platform_family"] +when "debian" + package "xfsdump" + package "xfslibs-dev" +when "rhel", "fedora" + package"xfsprogs-devel" +end diff --git a/chef/cookbooks/yum/.kitchen.yml b/chef/cookbooks/yum/.kitchen.yml new file mode 100644 index 0000000..8c7770a --- /dev/null +++ b/chef/cookbooks/yum/.kitchen.yml @@ -0,0 +1,22 @@ +--- +driver_plugin: vagrant +driver_config: + require_chef_omnibus: true + +platforms: +- name: centos-6.4 + driver_config: + box: opscode-centos-6.4 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box + +- name: centos-5.9 + driver_config: + box: opscode-centos-5.9 + box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-5.9_provisionerless.box + +suites: +- name: test + run_list: + - recipe[minitest-handler] + - recipe[yum::test] + attributes: {} diff --git a/chef/cookbooks/yum/Berksfile b/chef/cookbooks/yum/Berksfile new file mode 100644 index 0000000..6a8a4a9 --- /dev/null +++ b/chef/cookbooks/yum/Berksfile @@ -0,0 +1,7 @@ +site :opscode + +metadata + +group :integration do + cookbook "minitest-handler" +end diff --git a/chef/cookbooks/yum/CHANGELOG.md b/chef/cookbooks/yum/CHANGELOG.md new file mode 100644 index 0000000..0763d67 --- /dev/null +++ b/chef/cookbooks/yum/CHANGELOG.md @@ -0,0 +1,112 @@ +yum Cookbook CHANGELOG +====================== +This file is used to list changes made in each version of the yum cookbook. + + +v2.3.2 +------ +### Bug +- **[COOK-3145](https://tickets.opscode.com/browse/COOK-3145)** - Use correct download URL for epel `key_url` + +v2.3.0 +------ +### New Feature +- [COOK-2924]: Yum should allow type setting in repo file + +v2.2.4 +------ +### Bug +- [COOK-2360]: last commit to `yum_repository` changes previous behaviour +- [COOK-3015]: Yum cookbook test minitest to fail + +v2.2.2 +------ +### Improvement +- [COOK-2741]: yum::elrepo +- [COOK-2946]: update tests, test kitchen support in yum cookbook + +### Bug +- [COOK-2639]: Yum cookbook - epel - always assumes url is a mirror list +- [COOK-2663]: Yum should allow metadata_expire setting in repo file +- [COOK-2751]: Update yum.ius_release version to 1.0-11 + +v2.2.0 +------ +- [COOK-2189] - yum::ius failed on install (caused from rpm dependency) +- [COOK-2196] - Make includepkgs and exclude configurable for each repos +- [COOK-2244] - Allow configuring caching using attributes +- [COOK-2399] - yum cookbook LWRPs fail FoodCritic +- [COOK-2519] - Add priority option to Yum repo files +- [COOK-2593] - allow integer or string for yum priority +- [COOK-2643] - don't use conditional attribute for `yum_key` `remote_file` + +v2.1.0 +------ +- [COOK-2045] - add remi repository recipe +- [COOK-2121] - add `:create` action to `yum_repository` + +v2.0.6 +------ +- [COOK-2037] - minor style fixes +- [COOK-2038] - updated README + +v2.0.4 +------ +- [COOK-1908] - unable to install repoforge on CentOS 6 32 bit + +v2.0.2 +------ +- [COOK-1758] - Add default action for repository resource + +v2.0.0 +------ +This version changes the behavior of the EPEL recipe (most commonly used in other Opscode cookbooks) on Amazon, and removes an attribute, `node['yum']['epel_release']`. See the README for details. + +- [COOK-1772] - Simplify management of EPEL with LWRP + +v1.0.0 +------ +`mirrorlist` in the `yum_repository` LWRP must be set to the mirror list URI to use rather than setting it to true. See README.md. + +- [COOK-1088] - use dl.fedoraproject.org for EPEL to prevent redirects +- [COOK-1653] - fix mirrorlist +- [COOK-1710] - support http proxy +- [COOK-1722] - update IUS version + +v0.8.2 +------ +- [COOK-1521] - add :update action to `yum_repository` + +v0.8.0 +------ +- [COOK-1204] - Make 'add' default action for yum_repository +- [COOK-1351] - option to not make the yum cache (via attribute) +- [COOK-1353] - x86_64 centos path fixes +- [COOK-1414] - recipe for repoforge + +v0.6.2 +------ +- Updated README to remove git diff artifacts. + +v0.6.0 +------ +- Default action for the yum_repository LWRP is now add. +- [COOK-1227] - clear Chefs internal cache after adding new yum repo +- [COOK-1262] - yum::epel should enable existing repo on Amazon Linux +- [COOK-1272], [COOK-1302] - update RPM file for CentOS / RHEL 6 +- [COOK-1330] - update cookbook documentation on excludes for yum +- [COOK-1346] - retry remote_file for EPEL in case we get an FTP mirror + + +v0.5.2 +------ +- [COOK-825] - epel and ius `remote_file` should notify the `rpm_package` to install + +v0.5.0 +------ +- [COOK-675] - add recipe for handling EPEL repository +- [COOK-722] - add recipe for handling IUS repository + +v.0.1.2 +------ +- Remove yum update in default recipe, that doesn't update caches, it updates packages installed. diff --git a/chef/cookbooks/yum/CONTRIBUTING.md b/chef/cookbooks/yum/CONTRIBUTING.md new file mode 100644 index 0000000..3a99897 --- /dev/null +++ b/chef/cookbooks/yum/CONTRIBUTING.md @@ -0,0 +1,257 @@ +# Contributing to Opscode Cookbooks + +We are glad you want to contribute to Opscode Cookbooks! The first +step is the desire to improve the project. + +You can find the answers to additional frequently asked questions +[on the wiki](http://wiki.opscode.com/display/chef/How+to+Contribute). + +You can find additional information about +[contributing to cookbooks](http://wiki.opscode.com/display/chef/How+to+Contribute+to+Opscode+Cookbooks) +on the wiki as well. + +## Quick-contribute + +* Create an account on our [bug tracker](http://tickets.opscode.com) +* Sign our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L) +(keep reading if you're contributing on behalf of your employer) +* Create a ticket for your change on the + [bug tracker](http://tickets.opscode.com) +* Link to your patch as a rebased git branch or pull request from the + ticket +* Resolve the ticket as fixed + +We regularly review contributions and will get back to you if we have +any suggestions or concerns. + +## The Apache License and the CLA/CCLA + +Licensing is very important to open source projects, it helps ensure +the software continues to be available under the terms that the author +desired. Chef uses the Apache 2.0 license to strike a balance between +open contribution and allowing you to use the software however you +would like to. + +The license tells you what rights you have that are provided by the +copyright holder. It is important that the contributor fully +understands what rights they are licensing and agrees to them. +Sometimes the copyright holder isn't the contributor, most often when +the contributor is doing work for a company. + +To make a good faith effort to ensure these criteria are met, Opscode +requires a Contributor License Agreement (CLA) or a Corporate +Contributor License Agreement (CCLA) for all contributions. This is +without exception due to some matters not being related to copyright +and to avoid having to continually check with our lawyers about small +patches. + +It only takes a few minutes to complete a CLA, and you retain the +copyright to your contribution. + +You can complete our contributor agreement (CLA) +[ online](https://secure.echosign.com/public/hostedForm?formid=PJIF5694K6L). +If you're contributing on behalf of your employer, have your employer +fill out our +[Corporate CLA](https://secure.echosign.com/public/hostedForm?formid=PIE6C7AX856) +instead. + +## Ticket Tracker (JIRA) + +The [ticket tracker](http://tickets.opscode.com) is the most important +documentation for the code base. It provides significant historical +information, such as: + +* Which release a bug fix is included in +* Discussion regarding the design and merits of features +* Error output to aid in finding similar bugs + +Each ticket should aim to fix one bug or add one feature. + +## Using git + +You can get a quick copy of the repository for this cookbook by +running `git clone +git://github.com/opscode-coobkooks/COOKBOOKNAME.git`. + +For collaboration purposes, it is best if you create a Github account +and fork the repository to your own account. Once you do this you will +be able to push your changes to your Github repository for others to +see and use. + +If you have another repository in your GitHub account named the same +as the cookbook, we suggest you suffix the repository with -cookbook. + +### Branches and Commits + +You should submit your patch as a git branch named after the ticket, +such as COOK-1337. This is called a _topic branch_ and allows users to +associate a branch of code with the ticket. + +It is a best practice to have your commit message have a _summary +line_ that includes the ticket number, followed by an empty line and +then a brief description of the commit. This also helps other +contributors understand the purpose of changes to the code. + + [COOK-1757] - platform_family and style + + * use platform_family for platform checking + * update notifies syntax to "resource_type[resource_name]" instead of + resources() lookup + * COOK-692 - delete config files dropped off by packages in conf.d + * dropped debian 4 support because all other platforms have the same + values, and it is older than "old stable" debian release + +Remember that not all users use Chef in the same way or on the same +operating systems as you, so it is helpful to be clear about your use +case and change so they can understand it even when it doesn't apply +to them. + +### Github and Pull Requests + +All of Opscode's open source cookbook projects are available on +[Github](http://www.github.com/opscode-cookbooks). + +We don't require you to use Github, and we will even take patch diffs +attached to tickets on the tracker. However Github has a lot of +convenient features, such as being able to see a diff of changes +between a pull request and the main repository quickly without +downloading the branch. + +If you do choose to use a pull request, please provide a link to the +pull request from the ticket __and__ a link to the ticket from the +pull request. Because pull requests only have two states, open and +closed, we can't easily filter pull requests that are waiting for a +reply from the author for various reasons. + +### More information + +Additional help with git is available on the +[Working with Git](http://wiki.opscode.com/display/chef/Working+with+Git) +wiki page. + +## Functional and Unit Tests + +This cookbook is set up to run tests under +[Opscode's test-kitchen](https://github.com/opscode/test-kitchen). It +uses minitest-chef to run integration tests after the node has been +converged to verify that the state of the node. + +Test kitchen should run completely without exception using the default +[baseboxes provided by Opscode](https://github.com/opscode/bento). +Because Test Kitchen creates VirtualBox machines and runs through +every configuration in the Kitchenfile, it may take some time for +these tests to complete. + +If your changes are only for a specific recipe, run only its +configuration with Test Kitchen. If you are adding a new recipe, or +other functionality such as a LWRP or definition, please add +appropriate tests and ensure they run with Test Kitchen. + +If any don't pass, investigate them before submitting your patch. + +Any new feature should have unit tests included with the patch with +good code coverage to help protect it from future changes. Similarly, +patches that fix a bug or regression should have a _regression test_. +Simply put, this is a test that would fail without your patch but +passes with it. The goal is to ensure this bug doesn't regress in the +future. Consider a regular expression that doesn't match a certain +pattern that it should, so you provide a patch and a test to ensure +that the part of the code that uses this regular expression works as +expected. Later another contributor may modify this regular expression +in a way that breaks your use cases. The test you wrote will fail, +signalling to them to research your ticket and use case and accounting +for it. + +If you need help writing tests, please ask on the Chef Developer's +mailing list, or the #chef-hacking IRC channel. + +## Code Review + +Opscode regularly reviews code contributions and provides suggestions +for improvement in the code itself or the implementation. + +We find contributions by searching the ticket tracker for _resolved_ +tickets with a status of _fixed_. If we have feedback we will reopen +the ticket and you should resolve it again when you've made the +changes or have a response to our feedback. When we believe the patch +is ready to be merged, we will tag the _Code Reviewed_ field with +_Reviewed_. + +Depending on the project, these tickets are then merged within a week +or two, depending on the current release cycle. + +## Release Cycle + +The versioning for Opscode Cookbook projects is X.Y.Z. + +* X is a major release, which may not be fully compatible with prior + major releases +* Y is a minor release, which adds both new features and bug fixes +* Z is a patch release, which adds just bug fixes + +A released version of a cookbook will end in an even number, e.g. +"1.2.4" or "0.8.0". When development for the next version of the +cookbook begins, the "Z" patch number is incremented to the next odd +number, however the next release of the cookbook may be a major or +minor incrementing version. + +Releases of Opscode's cookbooks are usually announced on the Chef user +mailing list. Releases of several cookbooks may be batched together +and announced on the [Opscode Blog](http://www.opscode.com/blog). + +## Working with the community + +These resources will help you learn more about Chef and connect to +other members of the Chef community: + +* [chef](http://lists.opscode.com/sympa/info/chef) and + [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing + lists +* #chef and #chef-hacking IRC channels on irc.freenode.net +* [Community Cookbook site](http://community.opscode.com) +* [Chef wiki](http://wiki.opscode.com/display/chef) +* Opscode Chef [product page](http://www.opscode.com/chef) + + +## Cookbook Contribution Do's and Don't's + +Please do include tests for your contribution. If you need help, ask +on the +[chef-dev mailing list](http://lists.opscode.com/sympa/info/chef-dev) +or the +[#chef-hacking IRC channel](http://community.opscode.com/chat/chef-hacking). +Not all platforms that a cookbook supports may be supported by Test +Kitchen. Please provide evidence of testing your contribution if it +isn't trivial so we don't have to duplicate effort in testing. Chef +10.14+ "doc" formatted output is sufficient. + +Please do indicate new platform (families) or platform versions in the +commit message, and update the relevant ticket. + +If a contribution adds new platforms or platform versions, indicate +such in the body of the commit message(s), and update the relevant +COOK ticket. When writing commit messages, it is helpful for others if +you indicate the COOK ticket. For example: + + git commit -m '[COOK-1041] - Updated pool resource to correctly + delete.' + +Please do use [foodcritic](http://acrmp.github.com/foodcritic) to +lint-check the cookbook. Except FC007, it should pass all correctness +rules. FC007 is okay as long as the dependent cookbooks are *required* +for the default behavior of the cookbook, such as to support an +uncommon platform, secondary recipe, etc. + +Please do ensure that your changes do not break or modify behavior for +other platforms supported by the cookbook. For example if your changes +are for Debian, make sure that they do not break on CentOS. + +Please do not modify the version number in the metadata.rb, Opscode +will select the appropriate version based on the release cycle +information above. + +Please do not update the CHANGELOG.md for a new version. Not all +changes to a cookbook may be merged and released in the same versions. +Opscode will update the CHANGELOG.md when releasing a new version of +the cookbook. diff --git a/chef/cookbooks/yum/LICENSE b/chef/cookbooks/yum/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/chef/cookbooks/yum/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/cookbooks/yum/README.md b/chef/cookbooks/yum/README.md new file mode 100644 index 0000000..65d6b2a --- /dev/null +++ b/chef/cookbooks/yum/README.md @@ -0,0 +1,222 @@ +yum Cookbook +============ +Configures various YUM components on Red Hat-like systems. Includes LWRP for managing repositories and their GPG keys. + +Based on the work done by Eric Wolfe and Charles Duffy on the [yumrepo](https://github.com/atomic-penguin/cookbook-yumrepo) cookbook. + + +Requirements +------------ +Red Hat Enterprise Linux 5, and 6 distributions within this platform family. + + +Attributes +---------- +* `yum['exclude']` + - An array containing a list of packages to exclude from updates or installs. Wildcards and shell globs are supported. + - Defaults to an empty exclude list. + +* `yum['installonlypkgs']` + - An array containing a list of packages which should only be + installed, never updated. + - Defaults to an empty install-only list. + +* `yum['ius_release']` + - Set the IUS release to install. + - Defaults to the current release of the IUS repo. + +* `yum['repoforge_release']` + - Set the RepoForge release to install. + - Defaults to the current release of the repoforge repo. + +EPEL attributes used in the `yum::epel` recipe, see `attributes/epel.rb` for default values: + +* `yum['epel']['key']` + - Name of the GPG key used for the repo. + +* `yum['epel']['baseurl']` + - Base URL to an EPEL mirror. + +* `yum['epel']['url']` + - URL to the EPEL mirrorlist. + +* `yum['epel']['key_url']` + - URL to the GPG key for the repo. + +* `yum['epel']['includepkgs']` + - list of packages you want to use for the repo. + +* `yum['epel']['exclude']` + - list of packages you do NOT want to use for the repo. + +The `node['yum']['epel_release']` attribute is removed, see the __epel__ recipe information below. + +remi attributes used in the `yum::remi` recipe, see `attributes/remi.rb` for default values: + +* `yum['remi']['key']` + - Name of the GPG key used for the repo. + +* `yum['remi']['url']` + - URL to the remi mirrorlist. + +* `yum['remi']['key_url']` + - URL to the GPG key for the repo. + +* `yum['remi']['includepkgs']` + - list of packages you want to use for the repo. + +* `yum['remi']['exclude']` + - list of packages you do NOT want to use for the repo. + +Proxy settings used in yum.conf on RHEL family 5 and 6: + +* `yum['proxy']` + - Set the URL for an HTTP proxy + - None of the proxy settings are used if this is an empty string + (default) + +* `yum['proxy_username']` + - Set the username for the proxy + - not used if `yum['proxy']` above is an empty string + +* `yum['proxy_password']` + - Set the password for the proxy + - not used if `yum['proxy']` above is an empty string + + +Recipes +------- +### default +The default recipe does nothing. + +### yum +Manages the configuration of the `/etc/yum.conf` via attributes. See the aforementioned Array attributes `yum['exclude']` and `yum['installonlypkgs']`. + +### epel +Uses the `yum_key` and `yum_repository` resources from this cookbook are used to manage the main EPEL repository. If you need other EPEL repositories (source, debug-info), use the `yum_repository` LWRP in your own cookbook where those packages are required. The recipe will use the `yum['epel']` attributes (see above) to configure the key, url and download the GPG key for the repo. The defaults are detected by platform and version and should just work without modification in most use cases. + +On all platforms except Amazon, the action is to add the repository. On Amazon, the action is add and update. + +Amazon Linux has the EPEL repositories already added in the AMI. In previous versions of this cookbook, they were enabled with `yum-config-manager`, however in the current version, we manage the repository using the LWRP. The main difference is that the source and debuginfo repositories are not available, but if they're needed, add them using the `yum_repository` LWRP in your own cookbook(s). + +### ius +Installs the [IUS Community repositories](http://iuscommunity.org/Repos) via RPM. Uses the `node['yum']['ius_release']` attribute to select the right version of the package to install. + +The IUS repository requires EPEL, and includes `yum::epel` as a dependency. + +### repoforge +Installs the [RepoForge repositories](http://repoforge.org/) via RPM. Uses the `node['yum']['repoforge_release']` attribute to select the right version of the package to install. + +The RepoForge repository requires EPEL, and includes `yum::epel` as a dependency. + +### remi +Install the [Les RPM de Remi - Repository](http://rpms.famillecollet.com/) with the `yum_key` and `yum_repository` resources from this cookbook are used to manage the remi repository. Use the `yum['remi']` attributes (see above) to configure the key, url and download the GPG key for the repo. The defaults are detected by platform and should just work without modification in most use cases. + + +Resources/Providers +------------------- +### yum_key +This LWRP handles importing GPG keys for YUM repositories. Keys can be imported by the `url` parameter or placed in `/etc/pki/rpm-gpg/` by a recipe and then installed with the LWRP without passing the URL. + +#### Actions +- :add: installs the GPG key into `/etc/pki/rpm-gpg/` +- :remove: removes the GPG key from `/etc/pki/rpm-gpg/` + +#### Attribute Parameters +- key: name attribute. The name of the GPG key to install. +- url: if the key needs to be downloaded, the URL providing the download. + +#### Example + +``` ruby +# add the Zenoss GPG key +yum_key "RPM-GPG-KEY-zenoss" do + url "http://dev.zenoss.com/yum/RPM-GPG-KEY-zenoss" + action :add +end + +# remove Zenoss GPG key +yum_key "RPM-GPG-KEY-zenoss" do + action :remove +end +``` + +### yum_repository +This LWRP provides an easy way to manage additional YUM repositories. GPG keys can be managed with the `yum_key` LWRP. The LWRP automatically updates the package management cache upon the first run, when a new repo is added. + +#### Actions +- :create: creates a repository file and builds the repository listing +- :add: runs create action if repository file is missing (default) +- :remove: removes the repository file +- :update: updates the repository + +#### Attribute Parameters +- repo_name: name attribute. The name of the channel to discover +- description. The description of the repository +- url: The URL providing the packages, used for baseurl in the config +- mirrorlist: Set this as a string containing the URI to the + mirrorlist, start with "http://", "ftp://", "file://"; use "file://" + if the mirrorlist is a text file on the system. +- key: Optional, the name of the GPG key file installed by the `key` + LWRP. +- enabled: Default is `1`, set to `0` if the repository is disabled. +- type: Optional, alternate type of repository +- failovermethod: Optional, failovermethod +- bootstrapurl: Optional, bootstrapurl +- make_cache: Optional, Default is `true`, if `false` then `yum -q + makecache` will not be ran +- metadata_expire: Optional, Default is nil (or not applied) +- type: Optional, Default is nil (or not applied) + +*Note*: When using both url (to set baseurl) and mirrorlist, it is probably a good idea to also install the fastestmirror plugin, and use failovermethod "priority". + +#### Example +``` ruby +# add the Zenoss repository +yum_repository "zenoss" do + repo_name "zenoss" + description "Zenoss Stable repo" + url "http://dev.zenoss.com/yum/stable/" + key "RPM-GPG-KEY-zenoss" + action :add +end + +# remove Zenoss repo +yum_repository "zenoss" do + action :remove +end +``` + + +Usage +----- +Put `recipe[yum::yum]` in the run list to ensure yum is configured correctly for your environment within your Chef run. + +Use the `yum::epel` recipe to enable EPEL, or the `yum::ius` recipe to enable IUS, or the `yum::repoforge` recipe to enable RepoForge, or the `yum::remi` recipe to enable remi per __Recipes__ section above. + +You can manage GPG keys either with cookbook_file in a recipe if you want to package it with a cookbook or use the `url` parameter of the `key` LWRP. + + +License & Authors +----------------- +- Author:: Eric G. Wolfe +- Author:: Matt Ray () +- Author:: Joshua Timberman () + +```text +Copyright:: 2010 Tippr Inc. +Copyright:: 2011 Eric G. Wolfe +Copyright:: 2011-2012 Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/chef/cookbooks/yum/attributes/default.rb b/chef/cookbooks/yum/attributes/default.rb new file mode 100644 index 0000000..085ac99 --- /dev/null +++ b/chef/cookbooks/yum/attributes/default.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: yum +# Attributes:: default +# +# Copyright 2011, Eric G. Wolfe +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Example: override.yum.exclude = "kernel* compat-glibc*" +default['yum']['exclude'] = Array.new +default['yum']['installonlypkgs'] = Array.new +default['yum']['ius_release'] = '1.0-11' +default['yum']['repoforge_release'] = '0.5.2-2' +default['yum']['proxy'] = '' +default['yum']['proxy_username'] = '' +default['yum']['proxy_password'] = '' +default['yum']['cachedir'] = '/var/cache/yum' +default['yum']['keepcache'] = 0 diff --git a/chef/cookbooks/yum/attributes/elrepo.rb b/chef/cookbooks/yum/attributes/elrepo.rb new file mode 100644 index 0000000..5c97402 --- /dev/null +++ b/chef/cookbooks/yum/attributes/elrepo.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: yum +# Attributes:: elrepo +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['yum']['elrepo']['url'] = "http://elrepo.org/mirrors-elrepo.el#{node['platform_version'].to_i}" +default['yum']['elrepo']['key'] = "RPM-GPG-KEY-elrepo.org" +default['yum']['elrepo']['key_url'] = "http://elrepo.org/#{node['yum']['elrepo']['key']}" +default['yum']['elrepo']['includepkgs'] = nil +default['yum']['elrepo']['exclude'] = nil diff --git a/chef/cookbooks/yum/attributes/epel.rb b/chef/cookbooks/yum/attributes/epel.rb new file mode 100644 index 0000000..c2247f8 --- /dev/null +++ b/chef/cookbooks/yum/attributes/epel.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: yum +# Attributes:: epel +# +# Copyright 2011, Eric G. Wolfe +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform'] +when "amazon" + default['yum']['epel']['url'] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch" + default['yum']['epel']['baseurl'] = "" + default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL-6" +else + default['yum']['epel']['url'] = "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-#{node['platform_version'].to_i}&arch=$basearch" + default['yum']['epel']['baseurl'] = "" + + if node['platform_version'].to_i >= 6 + default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL-6" + else + default['yum']['epel']['key'] = "RPM-GPG-KEY-EPEL" + end +end + +default['yum']['epel']['key_url'] = "http://dl.fedoraproject.org/pub/epel/#{node['yum']['epel']['key']}" +default['yum']['epel']['includepkgs'] = nil +default['yum']['epel']['exclude'] = nil diff --git a/chef/cookbooks/yum/attributes/remi.rb b/chef/cookbooks/yum/attributes/remi.rb new file mode 100644 index 0000000..b80ee2c --- /dev/null +++ b/chef/cookbooks/yum/attributes/remi.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: yum +# Attributes:: remi +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform'] +when "fedora" + default['yum']['remi']['url'] = "http://rpms.famillecollet.com/fedora/#{node['platform_version'].to_i}/remi/mirror" +else + default['yum']['remi']['url'] = "http://rpms.famillecollet.com/enterprise/#{node['platform_version'].to_i}/remi/mirror" +end + +default['yum']['remi']['key'] = "RPM-GPG-KEY-remi" +default['yum']['remi']['key_url'] = "http://rpms.famillecollet.com/#{node['yum']['remi']['key']}" +default['yum']['remi']['includepkgs'] = nil +default['yum']['remi']['exclude'] = nil diff --git a/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 b/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 new file mode 100644 index 0000000..87a5035 --- /dev/null +++ b/chef/cookbooks/yum/files/default/RPM-GPG-KEY-EPEL-6 @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.5 (GNU/Linux) + +mQINBEvSKUIBEADLGnUj24ZVKW7liFN/JA5CgtzlNnKs7sBg7fVbNWryiE3URbn1 +JXvrdwHtkKyY96/ifZ1Ld3lE2gOF61bGZ2CWwJNee76Sp9Z+isP8RQXbG5jwj/4B +M9HK7phktqFVJ8VbY2jfTjcfxRvGM8YBwXF8hx0CDZURAjvf1xRSQJ7iAo58qcHn +XtxOAvQmAbR9z6Q/h/D+Y/PhoIJp1OV4VNHCbCs9M7HUVBpgC53PDcTUQuwcgeY6 +pQgo9eT1eLNSZVrJ5Bctivl1UcD6P6CIGkkeT2gNhqindRPngUXGXW7Qzoefe+fV +QqJSm7Tq2q9oqVZ46J964waCRItRySpuW5dxZO34WM6wsw2BP2MlACbH4l3luqtp +Xo3Bvfnk+HAFH3HcMuwdaulxv7zYKXCfNoSfgrpEfo2Ex4Im/I3WdtwME/Gbnwdq +3VJzgAxLVFhczDHwNkjmIdPAlNJ9/ixRjip4dgZtW8VcBCrNoL+LhDrIfjvnLdRu +vBHy9P3sCF7FZycaHlMWP6RiLtHnEMGcbZ8QpQHi2dReU1wyr9QgguGU+jqSXYar +1yEcsdRGasppNIZ8+Qawbm/a4doT10TEtPArhSoHlwbvqTDYjtfV92lC/2iwgO6g +YgG9XrO4V8dV39Ffm7oLFfvTbg5mv4Q/E6AWo/gkjmtxkculbyAvjFtYAQARAQAB +tCFFUEVMICg2KSA8ZXBlbEBmZWRvcmFwcm9qZWN0Lm9yZz6JAjYEEwECACAFAkvS +KUICGw8GCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRA7Sd8qBgi4lR/GD/wLGPv9 +qO39eyb9NlrwfKdUEo1tHxKdrhNz+XYrO4yVDTBZRPSuvL2yaoeSIhQOKhNPfEgT +9mdsbsgcfmoHxmGVcn+lbheWsSvcgrXuz0gLt8TGGKGGROAoLXpuUsb1HNtKEOwP +Q4z1uQ2nOz5hLRyDOV0I2LwYV8BjGIjBKUMFEUxFTsL7XOZkrAg/WbTH2PW3hrfS +WtcRA7EYonI3B80d39ffws7SmyKbS5PmZjqOPuTvV2F0tMhKIhncBwoojWZPExft +HpKhzKVh8fdDO/3P1y1Fk3Cin8UbCO9MWMFNR27fVzCANlEPljsHA+3Ez4F7uboF +p0OOEov4Yyi4BEbgqZnthTG4ub9nyiupIZ3ckPHr3nVcDUGcL6lQD/nkmNVIeLYP +x1uHPOSlWfuojAYgzRH6LL7Idg4FHHBA0to7FW8dQXFIOyNiJFAOT2j8P5+tVdq8 +wB0PDSH8yRpn4HdJ9RYquau4OkjluxOWf0uRaS//SUcCZh+1/KBEOmcvBHYRZA5J +l/nakCgxGb2paQOzqqpOcHKvlyLuzO5uybMXaipLExTGJXBlXrbbASfXa/yGYSAG +iVrGz9CE6676dMlm8F+s3XXE13QZrXmjloc6jwOljnfAkjTGXjiB7OULESed96MR +XtfLk0W5Ab9pd7tKDR6QHI7rgHXfCopRnZ2VVQ== +=V/6I +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/chef/cookbooks/yum/files/default/tests/minitest/default_test.rb b/chef/cookbooks/yum/files/default/tests/minitest/default_test.rb new file mode 100644 index 0000000..76f1a14 --- /dev/null +++ b/chef/cookbooks/yum/files/default/tests/minitest/default_test.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: yum +# Recipe:: default +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "yum::default" do + include Helpers::YumTest + + it "Default recipe does nothing, so default_test does nothing" do + skip "Default recipe does nothing so default test does nothing" + end +end diff --git a/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb b/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb new file mode 100644 index 0000000..cbc099e --- /dev/null +++ b/chef/cookbooks/yum/files/default/tests/minitest/support/helpers.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: yum_test +# Recipe:: default +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Helpers + module YumTest + require 'chef/mixin/shell_out' + include Chef::Mixin::ShellOut + include MiniTest::Chef::Assertions + include MiniTest::Chef::Context + include MiniTest::Chef::Resources + + # This isn't the most efficient thing in the world, but it works + # reliably as yum will only return the repos that are actually + # enabled. It would probably be more efficient, since we're at the + # end of the successful run, to cache the output to a file and + # inspect its contents. + def repo_enabled(repo) + shell_out("yum repolist enabled --verbose | grep Repo-id").stdout.include?(repo) + end + end +end diff --git a/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb b/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb new file mode 100644 index 0000000..3db9fe2 --- /dev/null +++ b/chef/cookbooks/yum/files/default/tests/minitest/test_test.rb @@ -0,0 +1,66 @@ +# +# Cookbook Name:: yum +# +# Copyright 2013, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path('../support/helpers', __FILE__) + +describe "yum::test" do + # helpers includes the repo_enabled method used to test that repos + # are in fact enabled. + include Helpers::YumTest + + describe "elrepo" do + it "enables the elrepo repository" do + assert(repo_enabled("elrepo")) + end + end + + describe "epel" do + it "enables the epel repository" do + assert(repo_enabled("epel")) + end + end + + describe "ius" do + it "enables the ius repository" do + assert(repo_enabled("ius")) + end + end + + describe "remi" do + it "enables the remi repository" do + assert(repo_enabled("remi")) + end + end + + describe "repoforge" do + it "enables the repoforge repository" do + assert(repo_enabled("rpmforge")) + end + end + + describe "cook-2121" do + + it 'doesnt update the zenos-add.repo file if it exists' do + assert File.zero?('/etc/yum.repos.d/zenoss-add.repo') + end + + it 'updates the zenoss-create file' do + file('/etc/yum.repos.d/zenoss-create.repo').must_match %r[baseurl=http://dev.zenoss.com/yum/stable/] + end + end +end diff --git a/chef/cookbooks/yum/metadata.rb b/chef/cookbooks/yum/metadata.rb new file mode 100644 index 0000000..a676ef6 --- /dev/null +++ b/chef/cookbooks/yum/metadata.rb @@ -0,0 +1,37 @@ +name "yum" +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "2.3.3" +recipe "yum", "Empty recipe." +recipe "yum::yum", "Manages yum configuration" + +%w{ redhat centos scientific amazon }.each do |os| + supports os, ">= 5.0" +end + +attribute "yum/exclude", + :display_name => "yum.conf exclude", + :description => "List of packages to exclude from updates or installs. This should be an array. Shell globs using wildcards (eg. * and ?) are allowed.", + :required => "optional" + +attribute "yum/installonlypkgs", + :display_name => "yum.conf installonlypkgs", + :description => "List of packages that should only ever be installed, never updated. Kernels in particular fall into this category. Defaults to kernel, kernel-smp, kernel-bigmem, kernel-enterprise, kernel-debug, kernel-unsupported.", + :required => "optional" + +attribute "yum/proxy", + :display_name => "yum.conf proxy", + :description => "Set the http URL for proxy to use in yum.conf", + :required => "optional" + +attribute "yum/proxy_username", + :display_name => "yum.conf proxy_username", + :description => "Set the proxy_username to use for yum.conf", + :required => "optional" + +attribute "yum/proxy_password", + :display_name => "yum.conf proxy_password", + :description => "Set the proxy_password to use for yum.conf", + :required => "optional" diff --git a/chef/cookbooks/yum/providers/key.rb b/chef/cookbooks/yum/providers/key.rb new file mode 100644 index 0000000..7a08bcf --- /dev/null +++ b/chef/cookbooks/yum/providers/key.rb @@ -0,0 +1,83 @@ +# +# Cookbook Name:: yum +# Provider:: key +# +# Copyright 2010, Tippr Inc. +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +def whyrun_supported? + true +end + +action :add do + unless ::File.exists?("/etc/pki/rpm-gpg/#{new_resource.key}") + Chef::Log.info "Adding #{new_resource.key} GPG key to /etc/pki/rpm-gpg/" + + if node['platform_version'].to_i <= 5 + package "gnupg" + elsif node['platform_version'].to_i >= 6 + package "gnupg2" + end + + execute "import-rpm-gpg-key-#{new_resource.key}" do + command "rpm --import /etc/pki/rpm-gpg/#{new_resource.key}" + action :nothing + not_if <<-EOH + function packagenames_for_keyfile() { + local filename="$1" + gpg \ + --with-fingerprint \ + --with-colons \ + --fixed-list-mode \ + "$filename" \ + | gawk -F: '/^pub/ { print tolower(sprintf("gpg-pubkey-%s-%x\\n", substr($5, length($5)-8+1), $6)) }' + } + + for pkgname in $(packagenames_for_keyfile "/etc/pki/rpm-gpg/#{new_resource.key}"); do + if [[ $pkgname ]] && ! rpm -q $pkgname ; then + exit 1; + fi; + done + + exit 0 + EOH + end + + #download the file if necessary + unless new_resource.url.nil? + # remote_file "/etc/pki/rpm-gpg/#{new_resource.key}" do + # source new_resource.url + # mode "0644" + # notifies :run, "execute[import-rpm-gpg-key-#{new_resource.key}]", :immediately + # end + cookbook_file "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6" do + source "RPM-GPG-KEY-EPEL-6" + mode 0644 + end + end + + end +end + +action :remove do + if ::File.exists?("/etc/pki/rpm-gpg/#{new_resource.key}") + Chef::Log.info "Removing #{new_resource.key} key from /etc/pki/rpm-gpg/" + file "/etc/pki/rpm-gpg/#{new_resource.key}" do + action :delete + end + new_resource.updated_by_last_action(true) + end +end diff --git a/chef/cookbooks/yum/providers/repository.rb b/chef/cookbooks/yum/providers/repository.rb new file mode 100644 index 0000000..87ee598 --- /dev/null +++ b/chef/cookbooks/yum/providers/repository.rb @@ -0,0 +1,125 @@ +# +# Cookbook Name:: yum +# Provider:: repository +# +# Copyright 2010, Tippr Inc. +# Copyright 2011, Opscode, Inc.. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# note that deletion does not remove GPG keys, either from the repo or +# /etc/pki/rpm-gpg; this is a design decision. + +def whyrun_supported? + true +end + +action :add do + unless ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") + Chef::Log.info "Adding #{new_resource.repo_name} repository to /etc/yum.repos.d/#{new_resource.repo_name}.repo" + repo_config + end +end + +action :create do + Chef::Log.info "Adding and updating #{new_resource.repo_name} repository in /etc/yum.repos.d/#{new_resource.repo_name}.repo" + repo_config +end + +action :remove do + if ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") + Chef::Log.info "Removing #{new_resource.repo_name} repository from /etc/yum.repos.d/" + file "/etc/yum.repos.d/#{new_resource.repo_name}.repo" do + action :delete + end + new_resource.updated_by_last_action(true) + end +end + +action :update do + repos ||= {} + # If the repo is already enabled/disabled as per the resource, we don't want to converge the template resource. + if ::File.exists?("/etc/yum.repos.d/#{new_resource.repo_name}.repo") + ::File.open("/etc/yum.repos.d/#{new_resource.repo_name}.repo") do |file| + repo_name ||= nil + file.each_line do |line| + case line + when /^\[(\S+)\]/ + repo_name = $1 + repos[repo_name] ||= {} + when /^(\S+?)=(.*)$/ + param, value = $1, $2 + repos[repo_name][param] = value + else + end + end + end + else + Chef::Log.error "Repo /etc/yum.repos.d/#{new_resource.repo_name}.repo does not exist, you must create it first" + end + if repos[new_resource.repo_name]['enabled'].to_i != new_resource.enabled + Chef::Log.info "Updating #{new_resource.repo_name} repository in /etc/yum.repos.d/#{new_resource.repo_name}.repo (setting enabled=#{new_resource.enabled})" + repo_config + else + Chef::Log.debug "Repository /etc/yum.repos.d/#{new_resource.repo_name}.repo is already set to enabled=#{new_resource.enabled}, skipping" + end +end + +private + +def repo_config + #import the gpg key. If it needs to be downloaded or imported from a cookbook + #that can be done in the calling recipe + if new_resource.key then + yum_key new_resource.key + end + #get the metadata + execute "yum-makecache" do + command "yum -q makecache" + action :nothing + end + #reload internal Chef yum cache + ruby_block "reload-internal-yum-cache" do + block do + Chef::Provider::Package::Yum::YumCache.instance.reload + end + action :nothing + end + #write out the file + template "/etc/yum.repos.d/#{new_resource.repo_name}.repo" do + cookbook "yum" + source "repo.erb" + mode "0644" + variables({ + :repo_name => new_resource.repo_name, + :description => new_resource.description, + :url => new_resource.url, + :mirrorlist => new_resource.mirrorlist, + :key => new_resource.key, + :enabled => new_resource.enabled, + :type => new_resource.type, + :failovermethod => new_resource.failovermethod, + :bootstrapurl => new_resource.bootstrapurl, + :includepkgs => new_resource.includepkgs, + :exclude => new_resource.exclude, + :priority => new_resource.priority, + :metadata_expire => new_resource.metadata_expire, + :type => new_resource.type + }) + if new_resource.make_cache + notifies :run, "execute[yum-makecache]", :immediately + notifies :create, "ruby_block[reload-internal-yum-cache]", :immediately + end + end +end diff --git a/chef/cookbooks/yum/recipes/default.rb b/chef/cookbooks/yum/recipes/default.rb new file mode 100644 index 0000000..9bc90f2 --- /dev/null +++ b/chef/cookbooks/yum/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: yum +# Recipe:: default +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/chef/cookbooks/yum/recipes/elrepo.rb b/chef/cookbooks/yum/recipes/elrepo.rb new file mode 100644 index 0000000..a54981d --- /dev/null +++ b/chef/cookbooks/yum/recipes/elrepo.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: yum +# Recipe:: elrepo +# +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +yum_key node['yum']['elrepo']['key'] do + url node['yum']['elrepo']['key_url'] + action :add +end + +yum_repository "elrepo" do + description "ELRepo.org Community Enterprise Linux Extras Repository" + key node['yum']['elrepo']['key'] + mirrorlist node['yum']['elrepo']['url'] + includepkgs node['yum']['elrepo']['includepkgs'] + exclude node['yum']['elrepo']['exclude'] + action :create +end diff --git a/chef/cookbooks/yum/recipes/epel.rb b/chef/cookbooks/yum/recipes/epel.rb new file mode 100644 index 0000000..be1e2aa --- /dev/null +++ b/chef/cookbooks/yum/recipes/epel.rb @@ -0,0 +1,35 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: yum +# Recipe:: epel +# +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# Copyright 2010, Eric G. Wolfe +# Copyright 2010, Tippr Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +yum_key node['yum']['epel']['key'] do + url node['yum']['epel']['key_url'] + action :add +end + +yum_repository "epel" do + description "Extra Packages for Enterprise Linux" + key node['yum']['epel']['key'] + url node['yum']['epel']['baseurl'] + mirrorlist node['yum']['epel']['url'] + includepkgs node['yum']['epel']['includepkgs'] + exclude node['yum']['epel']['exclude'] + action platform?('amazon') ? [:add, :update] : :add +end diff --git a/chef/cookbooks/yum/recipes/ius.rb b/chef/cookbooks/yum/recipes/ius.rb new file mode 100644 index 0000000..2d25290 --- /dev/null +++ b/chef/cookbooks/yum/recipes/ius.rb @@ -0,0 +1,42 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: yum +# Recipe:: ius +# +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "yum::epel" + +package "epel-release" + +major = node['platform_version'].to_i +ius = node['yum']['ius_release'] + +remote_file "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" do + source "http://dl.iuscommunity.org/pub/ius/stable/Redhat/#{major}/i386/ius-release-#{ius}.ius.el#{major}.noarch.rpm" + not_if "rpm -qa | grep -q '^ius-release-#{ius}'" + notifies :install, "rpm_package[ius-release]", :immediately +end + +rpm_package "ius-release" do + source "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" + only_if { ::File.exists?("#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm") } + action :nothing +end + +file "ius-release-cleanup" do + path "#{Chef::Config[:file_cache_path]}/ius-release-#{ius}.ius.el#{major}.noarch.rpm" + action :delete +end diff --git a/chef/cookbooks/yum/recipes/remi.rb b/chef/cookbooks/yum/recipes/remi.rb new file mode 100644 index 0000000..5ce5ff6 --- /dev/null +++ b/chef/cookbooks/yum/recipes/remi.rb @@ -0,0 +1,35 @@ +# +# Author:: Takeshi KOMIYA () +# Cookbook Name:: yum +# Recipe:: remi +# +# Copyright:: Copyright (c) 2011 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "yum::epel" + +yum_key node['yum']['remi']['key'] do + url node['yum']['remi']['key_url'] + action :add +end + +yum_repository "remi" do + description "Les RPM de remi pour Enterprise Linux #{node['platform_version']} - $basearch" + key node['yum']['remi']['key'] + mirrorlist node['yum']['remi']['url'] + failovermethod "priority" + includepkgs node['yum']['remi']['includepkgs'] + exclude node['yum']['remi']['exclude'] + action :create +end diff --git a/chef/cookbooks/yum/recipes/repoforge.rb b/chef/cookbooks/yum/recipes/repoforge.rb new file mode 100644 index 0000000..14c1607 --- /dev/null +++ b/chef/cookbooks/yum/recipes/repoforge.rb @@ -0,0 +1,41 @@ +# +# Author:: Eric Edgar () +# Cookbook Name:: yum +# Recipe:: repoforge +# +# Copyright:: Copyright (c) 2012-2013 Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "yum::epel" + +major = platform?("amazon") ? 6 : node['platform_version'].to_i +arch = (node['kernel']['machine'] == "i686" && major == 5) ? "i386" : node['kernel']['machine'] +repoforge = node['yum']['repoforge_release'] + +remote_file "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" do + source "http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" + not_if "rpm -qa | grep -q '^rpmforge-release-#{repoforge}'" + notifies :install, "rpm_package[rpmforge-release]", :immediately +end + +rpm_package "rpmforge-release" do + source "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" + only_if { ::File.exists?("#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm") } + action :install +end + +file "repoforge-release-cleanup" do + path "#{Chef::Config[:file_cache_path]}/rpmforge-release-#{repoforge}.el#{major}.rf.#{arch}.rpm" + action :delete +end diff --git a/chef/cookbooks/yum/recipes/test.rb b/chef/cookbooks/yum/recipes/test.rb new file mode 100644 index 0000000..7720c3a --- /dev/null +++ b/chef/cookbooks/yum/recipes/test.rb @@ -0,0 +1,39 @@ +# +# Cookbook:: yum +# Recipe:: test +# +# Author:: Joshua Timberman +# Copyright:: Copyright (c) 2013, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "yum::epel" +include_recipe "yum::elrepo" +include_recipe "yum::ius" +include_recipe "yum::repoforge" +include_recipe "yum::yum" +include_recipe "yum::remi" + +%w{add create}.each do |act| + file "/etc/yum.repos.d/zenoss-#{act}.repo" do + action :create + end + + yum_repository "zenoss-#{act}" do + description "Zenoss Stable repo" + url "http://dev.zenoss.com/yum/stable/" + key "RPM-GPG-KEY-zenoss" + action act.to_sym + end +end diff --git a/chef/cookbooks/yum/recipes/yum.rb b/chef/cookbooks/yum/recipes/yum.rb new file mode 100644 index 0000000..d353f50 --- /dev/null +++ b/chef/cookbooks/yum/recipes/yum.rb @@ -0,0 +1,23 @@ +# +# Cookbook Name:: yum +# Recipe:: yum +# +# Copyright 2011, Eric G. Wolfe +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +template "/etc/yum.conf" do + source "yum-rhel#{node['platform_version'].to_i}.conf.erb" +end diff --git a/chef/cookbooks/yum/resources/key.rb b/chef/cookbooks/yum/resources/key.rb new file mode 100644 index 0000000..96f9e72 --- /dev/null +++ b/chef/cookbooks/yum/resources/key.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: yum +# Resource:: key +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :remove +default_action :add + +attribute :key, :kind_of => String, :name_attribute => true +attribute :url, :kind_of => String, :default => nil + +def initialize(*args) + super + @action = :add +end diff --git a/chef/cookbooks/yum/resources/repository.rb b/chef/cookbooks/yum/resources/repository.rb new file mode 100644 index 0000000..1e274ca --- /dev/null +++ b/chef/cookbooks/yum/resources/repository.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: yum +# Resource:: repository +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :remove, :update, :create + +#name of the repo, used for .repo filename +attribute :repo_name, :kind_of => String, :name_attribute => true +attribute :description, :kind_of => String #long description +attribute :url, :kind_of => String, :default => "" +attribute :mirrorlist, :default => false +attribute :key, :kind_of => String, :default => nil +attribute :enabled, :default => 1 +attribute :type, :kind_of => String, :default => nil +attribute :failovermethod, :kind_of => String, :default => nil +attribute :bootstrapurl, :kind_of => String, :default => nil +attribute :make_cache, :kind_of => [TrueClass, FalseClass], :default => true +attribute :includepkgs, :kind_of => String, :default => nil +attribute :exclude, :kind_of => String, :default => nil +attribute :priority, :kind_of => [Integer, String], :default => nil +attribute :metadata_expire, :kind_of => [Integer, String], :default => nil +attribute :type, :kind_of => String, :default => nil + +def initialize(*args) + super + @action = :add +end diff --git a/chef/cookbooks/yum/templates/default/repo.erb b/chef/cookbooks/yum/templates/default/repo.erb new file mode 100644 index 0000000..02628a5 --- /dev/null +++ b/chef/cookbooks/yum/templates/default/repo.erb @@ -0,0 +1,41 @@ +# Generated by Chef for <%= node['fqdn'] %> +# Local modifications will be overwritten. +[<%= @repo_name %>] +name=<%= @description %> +<% if @type %> +type=<%= @type %> +<% end %> +<% unless @url.empty? -%> +baseurl=<%= @url %> +<% end -%> +<% if @mirrorlist %> +mirrorlist=<%= @mirrorlist %> +<% end %> +<% if @key %> +gpgcheck=1 +gpgkey=file:///etc/pki/rpm-gpg/<%= @key %> +<% else %> +gpgcheck=0 +<% end %> +enabled=<%= @enabled %> +<% if @failovermethod %> +failovermethod=<%= @failovermethod %> +<% end %> +<% if @bootstrapurl %> +bootstrapurl=<%= @bootstrapurl %> +<% end %> +<% if @includepkgs %> +includepkgs=<%= @includepkgs %> +<% end %> +<% if @exclude %> +exclude=<%= @exclude %> +<% end %> +<% if @priority %> +priority=<%= @priority %> +<% end %> +<% if @metadata_expire %> +metadata_expire=<%= @metadata_expire%> +<% end %> +<% if @type %> +type=<%= @type%> +<% end %> diff --git a/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb b/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb new file mode 100644 index 0000000..6ed58dc --- /dev/null +++ b/chef/cookbooks/yum/templates/default/yum-rhel5.conf.erb @@ -0,0 +1,33 @@ +# Generated by Chef for <%= node['fqdn'] %> +# Local modifications will be overwritten. +[main] +cachedir=<%= node['yum']['cachedir'] %> +keepcache=<%= node['yum']['keepcache'] %> +debuglevel=2 +logfile=/var/log/yum.log +distroverpkg=redhat-release +tolerant=1 +exactarch=1 +obsoletes=1 +gpgcheck=1 +plugins=1 +<%- unless node['yum']['exclude'].empty? %> +exclude=<%= node['yum']['exclude'].join(" ") %> +<%- end %> +<%- unless node['yum']['installonlypkgs'].empty? %> +installonlypkgs=<%= node['yum']['installonlypkgs'].join(" ") %> +<%- end %> +<%- unless node['yum']['proxy'].empty? %> +proxy=<%= node['yum']['proxy'] %> +proxy_username=<%= node['yum']['proxy_username'] %> +proxy_password=<%= node['yum']['proxy_password'] %> +<%- end %> + +# Note: yum-RHN-plugin doesn't honor this. +metadata_expire=1h + +# Default. +# installonly_limit = 3 + +# PUT YOUR REPOS HERE OR IN separate files named file.repo +# in /etc/yum.repos.d diff --git a/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb b/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb new file mode 100644 index 0000000..44d7979 --- /dev/null +++ b/chef/cookbooks/yum/templates/default/yum-rhel6.conf.erb @@ -0,0 +1,36 @@ +# Generated by Chef for <%= node['fqdn'] %> +# Local modifications will be overwritten. +[main] +cachedir=<%= node['yum']['cachedir'] %>/$basearch/$releasever +keepcache=<%= node['yum']['keepcache'] %> +debuglevel=2 +logfile=/var/log/yum.log +exactarch=1 +obsoletes=1 +gpgcheck=1 +plugins=1 +installonly_limit=3 +<%- unless node['yum']['exclude'].empty? %> +exclude=<%= node['yum']['exclude'].join(" ") %> +<%- end %> +<%- unless node['yum']['installonlypkgs'].empty? %> +installonlypkgs=<%= node['yum']['installonlypkgs'].join(" ") %> +<%- end %> +<%- unless node['yum']['proxy'].empty? %> +proxy=<%= node['yum']['proxy'] %> +proxy_username=<%= node['yum']['proxy_username'] %> +proxy_password=<%= node['yum']['proxy_password'] %> +<%- end %> + +# This is the default, if you make this bigger yum won't see if the metadata +# is newer on the remote and so you'll "gain" the bandwidth of not having to +# download the new metadata and "pay" for it by yum not having correct +# information. +# It is esp. important, to have correct metadata, for distributions like +# Fedora which don't keep old packages around. If you don't like this checking +# interupting your command line usage, it's much better to have something +# manually check the metadata once an hour (yum-updatesd will do this). +# metadata_expire=90m + +# PUT YOUR REPOS HERE OR IN separate files named file.repo +# in /etc/yum.repos.d diff --git a/chef/databags/openstack/openstack.json b/chef/databags/openstack/openstack.json new file mode 100644 index 0000000..818d2d5 --- /dev/null +++ b/chef/databags/openstack/openstack.json @@ -0,0 +1,211 @@ +{ "all_roles" : { "os-single-controller" : "openstack controller node", + "os-network" : "openstack network node", + "os-compute-worker" : "openstack nova node" + }, + "credential" : { "identity" : { "roles" : { "admin" : "admin", + "member" : "Member" + }, + "tenants" : { "admin" : "admin", + "service" : "service" + }, + "token" : { "admin" : "01234567890abc0987654321" }, + "users" : { "admin" : { "password" : "admin", + "tenant" : "admin", + "username" : "admin" + }, + "compute" : { "password" : "nova", + "tenant" : "service", + "username" : "nova" + }, + "image" : { "password" : "glance", + "tenant" : "service", + "username" : "glance" + }, + "metering" : { "password" : "ceilometer", + "tenant" : "service", + "username" : "ceilometer" + }, + "network" : { "password" : "quantum", + "tenant" : "service", + "username" : "quantum" + }, + "object-store" : { "password" : "swift", + "tenant" : "service", + "username" : "swift" + }, + "volume" : { "password" : "cinder", + "tenant" : "service", + "username" : "cinder" + } + } + }, + "metadata" : { "password" : "Hello_Openstack" }, + "mq" : { "rabbitmq" : { "password" : "guest", + "username" : "guest" + } }, + "mysql" : { "compute" : { "password" : "admin", + "username" : "nova" + }, + "dashboard" : { "password" : "admin", + "username" : "dashboard" + }, + "identity" : { "password" : "admin", + "username" : "keystone" + }, + "image" : { "password" : "admin", + "username" : "glance" + }, + "metering" : { "password" : "admin", + "username" : "ceilometer" + }, + "network" : { "password" : "admin", + "username" : "quantum" + }, + "super" : { "password" : "test", + "username" : "root" + }, + "volume" : { "password" : "admin", + "username" : "cinder" + } + }, + "text" : true + }, + "dashboard_roles" : [ "os-single-controller" ], + "db" : { "mysql" : { "bind_address" : "10.145.88.231", + "port" : "3306" + }, + "service_type" : "mysql" + }, + "endpoints" : { "compute" : { "metadata" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "novnc" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "service" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "xvpvnc" : { "host" : "10.145.88.231", + "scheme" : "http" + } + }, + "ec2" : { "admin" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } + }, + "identity" : { "admin" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } + }, + "image" : { "registry" : { "host" : "10.145.88.231", + "scheme" : "http" + }, + "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } + }, + "metering" : { "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } }, + "network" : { "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } }, + "volume" : { "service" : { "host" : "10.145.88.231", + "scheme" : "http" + } } + }, + "id" : "env_default", + "mq" : { "rabbitmq" : { "bind_address" : "10.145.88.231", + "port" : "5672" + }, + "service_type" : "rabbitmq" + }, + "networking" : { "control" : { "interface" : "eth0" }, + "nova" : { "network_type" : "quantum" }, + "plugins" : { "ovs" : { "gre" : { "bridge_mappings" : "nil", + "enable_tunneling" : "True", + "local_ip_interface" : "eth0", + "network_vlan_ranges" : "nil", + "tunnel_bridge" : "br-tun", + "tunnel_id_ranges" : "1:3000" + }, + "integration_bridge" : "br-int", + "status" : "enable", + "tenant_network_type" : "gre", + "vlan" : { "bridge_mappings" : "physnet1:br-eth1", + "enable_tunneling" : "False", + "local_ip_interface" : "nil", + "network_vlan_ranges" : "physnet1:1000:2999", + "tunnel_bridge" : "nil", + "tunnel_id_ranges" : "nil" + } + } }, + "public" : { "interface" : "eth1" }, + "storage" : { "interface" : "eth0" }, + "tenant" : { "interface" : "eth0" } + }, + "ntp" : { "ntpserver" : "0.centos.pool.ntp.org" }, + "release" : "grizzly", + "role_assign_policy" : { + "default" : { + "bundles" : [ ], + "exclusives" : [ "os-single-controller", "os-network" ], + "roles" : [ "os-single-controller", + "os-compute-worker", + "os-network" + ], + "default_min" : 1, + "default_max" : 1, + "maxs" : { "os-compute-worker": -1 } + }, + "policy_by_host_numbers" : { + "1" : { + "bundles" : [ [ + "os-single-controller", + "os-compute-worker", + "os-network" + ] ], + "exclusives" : [ ] + }, + "2" : { + "bundles" : [ [ + "os-compute-worker", + "os-network" + ] ], + "exclusives" : [ "os-single-controller" ] + } + } + }, + "support_oses": [ "CentOS*" ], + "services" : { "compute" : { "name" : "nova", + "status" : "enable" + }, + "dashboard" : { "name" : "horizon", + "status" : "enable" + }, + "identity" : { "name" : "keystone", + "status" : "enable" + }, + "image" : { "name" : "glance", + "status" : "enable" + }, + "metering" : { "name" : "ceilometer", + "status" : "disable" + }, + "network" : { "name" : "quantum", + "status" : "enable" + }, + "object-store" : { "name" : "swift", + "status" : "disable" + }, + "volume" : { "name" : "cinder", + "status" : "enable" + } + } +} diff --git a/chef/roles/allinone-compute.rb b/chef/roles/allinone-compute.rb new file mode 100644 index 0000000..daa008a --- /dev/null +++ b/chef/roles/allinone-compute.rb @@ -0,0 +1,6 @@ +name "allinone-compute" +description "This will deploy all of the services for Openstack Compute to function on a single box." +run_list( + "role[os-compute-single-controller]", + "role[os-compute-worker]" +) diff --git a/chef/roles/os-base.rb b/chef/roles/os-base.rb new file mode 100644 index 0000000..c2783be --- /dev/null +++ b/chef/roles/os-base.rb @@ -0,0 +1,6 @@ +name "os-base" +description "OpenStack Base role" +run_list( + "recipe[openstack-common]", + "recipe[openstack-common::logging]" + ) diff --git a/chef/roles/os-block-storage-api.rb b/chef/roles/os-block-storage-api.rb new file mode 100644 index 0000000..9b0ee1b --- /dev/null +++ b/chef/roles/os-block-storage-api.rb @@ -0,0 +1,6 @@ +name "os-block-storage-api" +description "OpenStack Block Storage API service" +run_list( + "role[os-base]", + "recipe[openstack-block-storage::api]" + ) diff --git a/chef/roles/os-block-storage-scheduler.rb b/chef/roles/os-block-storage-scheduler.rb new file mode 100644 index 0000000..b7de6b1 --- /dev/null +++ b/chef/roles/os-block-storage-scheduler.rb @@ -0,0 +1,6 @@ +name "os-block-storage-scheduler" +description "OpenStack Block Storage Scheduler service" +run_list( + "role[os-base]", + "recipe[openstack-block-storage::scheduler]" + ) diff --git a/chef/roles/os-block-storage-worker.rb b/chef/roles/os-block-storage-worker.rb new file mode 100644 index 0000000..bb0aa98 --- /dev/null +++ b/chef/roles/os-block-storage-worker.rb @@ -0,0 +1,6 @@ +name "os-block-storage-worker" +description "OpenStack Block Storage worker" +run_list( + "role[os-base]", + "recipe[openstack-block-storage::volume]" + ) diff --git a/chef/roles/os-block-storage.rb b/chef/roles/os-block-storage.rb new file mode 100644 index 0000000..f67aeed --- /dev/null +++ b/chef/roles/os-block-storage.rb @@ -0,0 +1,8 @@ +name "os-block-storage" +description "Configures OpenStack block storage, configured by attributes." +run_list( + "role[os-base]", + "recipe[openstack-block-storage::api]", + "recipe[openstack-block-storage::scheduler]", + "recipe[openstack-block-storage::volume]" + ) diff --git a/chef/roles/os-compute-api-ec2.rb b/chef/roles/os-compute-api-ec2.rb new file mode 100644 index 0000000..10731a7 --- /dev/null +++ b/chef/roles/os-compute-api-ec2.rb @@ -0,0 +1,6 @@ +name "os-compute-api-ec2" +description "EC2 API for Compute" +run_list( + "role[os-base]", + "recipe[openstack-compute::api-ec2]" + ) diff --git a/chef/roles/os-compute-api-metadata.rb b/chef/roles/os-compute-api-metadata.rb new file mode 100644 index 0000000..59180c5 --- /dev/null +++ b/chef/roles/os-compute-api-metadata.rb @@ -0,0 +1,6 @@ +name "os-compute-api-metadata" +description "OpenStack compute metadata API service" +run_list( + "role[os-base]", + "recipe[openstack-compute::api-metadata]" + ) diff --git a/chef/roles/os-compute-api-os-compute.rb b/chef/roles/os-compute-api-os-compute.rb new file mode 100644 index 0000000..9a3e021 --- /dev/null +++ b/chef/roles/os-compute-api-os-compute.rb @@ -0,0 +1,6 @@ +name "os-compute-api-os-compute" +description "OpenStack API for Compute" +run_list( + "role[os-base]", + "recipe[openstack-compute::api-os-compute]" + ) diff --git a/chef/roles/os-compute-api.rb b/chef/roles/os-compute-api.rb new file mode 100644 index 0000000..2db6422 --- /dev/null +++ b/chef/roles/os-compute-api.rb @@ -0,0 +1,7 @@ +name "os-compute-api" +description "Roll-up role for all the Compute APIs" +run_list( + "role[os-compute-api-ec2]", + "role[os-compute-api-os-compute]" +# "role[os-compute-api-metadata]" + ) diff --git a/chef/roles/os-compute-cert.rb b/chef/roles/os-compute-cert.rb new file mode 100644 index 0000000..ad9922c --- /dev/null +++ b/chef/roles/os-compute-cert.rb @@ -0,0 +1,6 @@ +name "os-compute-cert" +description "OpenStack Compute Cert service" +run_list( + "role[os-base]", + "recipe[openstack-compute::nova-cert]" + ) diff --git a/chef/roles/os-compute-controller.rb b/chef/roles/os-compute-controller.rb new file mode 100644 index 0000000..4606fd2 --- /dev/null +++ b/chef/roles/os-compute-controller.rb @@ -0,0 +1,11 @@ +name "os-compute-controller" +description "Roll-up role for all the Compute APIs" +run_list( + "role[os-base]", + "role[os-compute-api]", + "role[os-compute-scheduler]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "recipe[openstack-compute::conductor]", + "recipe[openstack-compute::nova-setup]" + ) diff --git a/chef/roles/os-compute-scheduler.rb b/chef/roles/os-compute-scheduler.rb new file mode 100644 index 0000000..2c75f39 --- /dev/null +++ b/chef/roles/os-compute-scheduler.rb @@ -0,0 +1,6 @@ +name "os-compute-scheduler" +description "Nova scheduler" +run_list( + "role[os-base]", + "recipe[openstack-compute::scheduler]" + ) diff --git a/chef/roles/os-compute-single-controller.rb b/chef/roles/os-compute-single-controller.rb new file mode 100644 index 0000000..c47366a --- /dev/null +++ b/chef/roles/os-compute-single-controller.rb @@ -0,0 +1,16 @@ +name "os-compute-single-controller" +description "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller." +run_list( + "role[os-base]", + "role[os-ops-database]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-image]", + "role[os-network]", + "role[os-compute-scheduler]", + "role[os-compute-api]", + "role[os-block-storage]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "role[os-dashboard]" + ) diff --git a/chef/roles/os-compute-vncproxy.rb b/chef/roles/os-compute-vncproxy.rb new file mode 100644 index 0000000..fd3eb08 --- /dev/null +++ b/chef/roles/os-compute-vncproxy.rb @@ -0,0 +1,7 @@ +name "os-compute-vncproxy" +description "Nova VNC Proxy" +run_list( + "role[os-base]", + "recipe[openstack-compute::vncproxy]" + ) + diff --git a/chef/roles/os-compute-worker.rb b/chef/roles/os-compute-worker.rb new file mode 100644 index 0000000..e960beb --- /dev/null +++ b/chef/roles/os-compute-worker.rb @@ -0,0 +1,7 @@ +name "os-compute-worker" +description "The compute node, most likely with a hypervisor." +run_list( + "role[os-base]", + "recipe[openstack-compute::compute]" + ) + diff --git a/chef/roles/os-dashboard.rb b/chef/roles/os-dashboard.rb new file mode 100644 index 0000000..d33437f --- /dev/null +++ b/chef/roles/os-dashboard.rb @@ -0,0 +1,7 @@ +name "os-dashboard" +description "Horizon server" +run_list( + "role[os-base]", +# "recipe[openstack-dashboard::db]", + "recipe[openstack-dashboard::server]" + ) diff --git a/chef/roles/os-identity-api-admin.rb b/chef/roles/os-identity-api-admin.rb new file mode 100644 index 0000000..86e46cf --- /dev/null +++ b/chef/roles/os-identity-api-admin.rb @@ -0,0 +1,7 @@ +name "os-identity-api-admin" +description "Keystone admin API service" +run_list( + "role[os-base]", + "recipe[openstack-identity::server]" + ) + diff --git a/chef/roles/os-identity-api.rb b/chef/roles/os-identity-api.rb new file mode 100644 index 0000000..96d7db9 --- /dev/null +++ b/chef/roles/os-identity-api.rb @@ -0,0 +1,6 @@ +name "os-identity-api" +description "Keystone API service" +run_list( + "role[os-base]", + "recipe[openstack-identity::server]" + ) diff --git a/chef/roles/os-identity.rb b/chef/roles/os-identity.rb new file mode 100644 index 0000000..07ea27b --- /dev/null +++ b/chef/roles/os-identity.rb @@ -0,0 +1,7 @@ +name "os-identity" +description "Roll-up role for Identity" +run_list( + "role[os-base]", + "recipe[openstack-identity::server]", + "recipe[openstack-identity::registration]" + ) diff --git a/chef/roles/os-image-api.rb b/chef/roles/os-image-api.rb new file mode 100644 index 0000000..b260025 --- /dev/null +++ b/chef/roles/os-image-api.rb @@ -0,0 +1,8 @@ +name "os-image-api" +description "Glance API service" +run_list( + "role[os-base]", + #"recipe[openstack-image::db]", + "recipe[openstack-image::api]" + ) + diff --git a/chef/roles/os-image-registry.rb b/chef/roles/os-image-registry.rb new file mode 100644 index 0000000..a4ff31f --- /dev/null +++ b/chef/roles/os-image-registry.rb @@ -0,0 +1,8 @@ +name "os-image-registry" +description "Glance Registry service" +run_list( + "role[os-base]", + #"recipe[openstack-image::db]", + "recipe[openstack-image::registry]" + ) + diff --git a/chef/roles/os-image.rb b/chef/roles/os-image.rb new file mode 100644 index 0000000..8299fa8 --- /dev/null +++ b/chef/roles/os-image.rb @@ -0,0 +1,6 @@ +name "os-image" +description "Roll-up role for Glance." +run_list( + "role[os-image-registry]", + "role[os-image-api]" + ) diff --git a/chef/roles/os-infra-caching.rb b/chef/roles/os-infra-caching.rb new file mode 100644 index 0000000..478227a --- /dev/null +++ b/chef/roles/os-infra-caching.rb @@ -0,0 +1,6 @@ +name "os-infra-caching" +description "Memcached role for Openstack" +run_list( + "role[os-base]", + "recipe[memcached::default]" + ) diff --git a/chef/roles/os-network-server.rb b/chef/roles/os-network-server.rb new file mode 100644 index 0000000..e7fd781 --- /dev/null +++ b/chef/roles/os-network-server.rb @@ -0,0 +1,6 @@ +name "os-network-server" +description "Configures OpenStack networking, managed by attribute for either nova-network or quantum" +run_list( + "role[os-base]", + "recipe[openstack-network::server]" + ) diff --git a/chef/roles/os-network.rb b/chef/roles/os-network.rb new file mode 100644 index 0000000..983736d --- /dev/null +++ b/chef/roles/os-network.rb @@ -0,0 +1,9 @@ +name "os-network" +description "Configures OpenStack networking, managed by attribute for either nova-network or quantum" +run_list( + "role[os-base]", + "recipe[openstack-network::openvswitch]", + "recipe[openstack-network::l3_agent]", + "recipe[openstack-network::dhcp_agent]", + "recipe[openstack-network::metadata_agent]" + ) diff --git a/chef/roles/os-object-storage-account.rb b/chef/roles/os-object-storage-account.rb new file mode 100644 index 0000000..7e59eed --- /dev/null +++ b/chef/roles/os-object-storage-account.rb @@ -0,0 +1,6 @@ +name "os-object-storage-account" +description "OpenStack object storage account service" +run_list( + "role[os-base]", + "recipe[openstack-object-storage::account]" + ) diff --git a/chef/roles/os-object-storage-container.rb b/chef/roles/os-object-storage-container.rb new file mode 100644 index 0000000..5ee8098 --- /dev/null +++ b/chef/roles/os-object-storage-container.rb @@ -0,0 +1,6 @@ +name "os-object-storage-container" +description "OpenStack object storage container service" +run_list( + "role[os-base]", + "recipe[openstack-object-storage::container]" + ) diff --git a/chef/roles/os-object-storage-management.rb b/chef/roles/os-object-storage-management.rb new file mode 100644 index 0000000..90399c3 --- /dev/null +++ b/chef/roles/os-object-storage-management.rb @@ -0,0 +1,6 @@ +name "os-object-storage-management" +description "OpenStack object storage management service" +run_list( + "role[os-base]", + "recipe[openstack-object-storage::management]" + ) diff --git a/chef/roles/os-object-storage-object.rb b/chef/roles/os-object-storage-object.rb new file mode 100644 index 0000000..1d15727 --- /dev/null +++ b/chef/roles/os-object-storage-object.rb @@ -0,0 +1,6 @@ +name "os-object-storage-object" +description "OpenStack object storage object service" +run_list( + "role[os-base]", + "recipe[openstack-object-storage::object]" + ) diff --git a/chef/roles/os-object-storage-proxy.rb b/chef/roles/os-object-storage-proxy.rb new file mode 100644 index 0000000..da660a7 --- /dev/null +++ b/chef/roles/os-object-storage-proxy.rb @@ -0,0 +1,6 @@ +name "os-object-storage-proxy" +description "OpenStack object storage proxy service" +run_list( + "role[os-base]", + "recipe[openstack-object-storage::proxy]" + ) diff --git a/chef/roles/os-object-storage.rb b/chef/roles/os-object-storage.rb new file mode 100644 index 0000000..2a06fc2 --- /dev/null +++ b/chef/roles/os-object-storage.rb @@ -0,0 +1,6 @@ +name "os-object-storage" +description "OpenStack object storage roll-up role" +run_list( + "role[os-base]", + "recipe[openstack-object-storage]" + ) diff --git a/chef/roles/os-ops-database.rb b/chef/roles/os-ops-database.rb new file mode 100644 index 0000000..51d22f6 --- /dev/null +++ b/chef/roles/os-ops-database.rb @@ -0,0 +1,7 @@ +name "os-ops-database" +description "Currently MySQL Server (non-ha)" +run_list( + "role[os-base]", + "recipe[openstack-ops-database::server]", + "recipe[openstack-ops-database::openstack-db]" + ) diff --git a/chef/roles/os-ops-messaging.rb b/chef/roles/os-ops-messaging.rb new file mode 100644 index 0000000..935a94f --- /dev/null +++ b/chef/roles/os-ops-messaging.rb @@ -0,0 +1,6 @@ +name "os-ops-messaging" +description "Currently RabbitMQ Server (non-ha)" +run_list( + "role[os-base]", + "recipe[openstack-ops-messaging::server]" + ) diff --git a/chef/roles/os-single-controller.rb b/chef/roles/os-single-controller.rb new file mode 100644 index 0000000..88ad74f --- /dev/null +++ b/chef/roles/os-single-controller.rb @@ -0,0 +1,18 @@ +name "os-single-controller" +description "Roll-up role for all of the OpenStack Compute services on a single, non-HA controller." +run_list( + "role[os-base]", + "role[os-ops-database]", + "role[os-ops-messaging]", + "role[os-identity]", + "role[os-image]", + "role[os-network-server]", + "role[os-block-storage]", + "role[os-compute-scheduler]", + "role[os-compute-api]", + "recipe[openstack-compute::conductor]", + "recipe[openstack-compute::nova-setup]", + "role[os-compute-cert]", + "role[os-compute-vncproxy]", + "role[os-dashboard]" + ) diff --git a/cobbler/kickstarts/default.ks b/cobbler/kickstarts/default.ks new file mode 100644 index 0000000..c3349f2 --- /dev/null +++ b/cobbler/kickstarts/default.ks @@ -0,0 +1,122 @@ +# Kickstart for Profile: CentOS6.4_x86-64-1 +# Distro: CentOS6.4 + +# System Authorization +auth --useshadow --enablemd5 + +# System Bootloader +bootloader --location=mbr + +# Clear MBR +zerombr + +# Pre-clear Partition +clearpart --all --initlabel + +# Use Text Mode +text +# cmdline + +# Disable Firewall +firewall --disabled + +# Run the Setup Agent on first-boot +firstboot --disable + +# System Keyboard +keyboard us + +# Language Setting +lang en_US + +# Installation Loggin Level +logging --level=info + +# Network Installation +url --url=$tree + + +$SNIPPET('network_config') + +# Repository Config +repo --name=ppa_repo --baseurl=http://$server:$http_port/cobbler/repo_mirror/ppa_repo/ + +# Root Password +#if $getVar('password', '') != "" +rootpw --iscrypted $password +#else +rootpw root +#end if + +# Selinux Disable +selinux --disabled + +# No X Window System +skipx + +# System Timezone +timezone --utc US/Pacific + +# Install +install + +# Reboot After Installation +reboot + +%include /tmp/part-include + +%pre +$SNIPPET('log_ks_pre') +$kickstart_start +$SNIPPET('pre_install_network_config') +# Enable installation monitoring +$SNIPPET('pre_anamon') +# useful to debug pre/post +# chvt 3 +# exec < /dev/tty3 > /dev/tty3 2>/dev/tty3 + +# get the number of hard disks and their names + +$SNIPPET('partition_disks') + +# Packages +# %packages --ignoremissing --nobase +%packages --nobase +@core +iproute +chef-11.8.0-1.el6.x86_64 +ntp +openssh-clients +wget + + +%post --log=/var/log/post_install.log +#if $getVar('passwd', '') != "" + #set $passwd = $passwd.strip() +/usr/sbin/useradd -p '$passwd' $user +#end if + +$SNIPPET('post_install_network_config') + +cat << EOF > /etc/yum.conf +$SNIPPET('yum.conf') +EOF + +chkconfig ntpd on +chkconfig iptables off +chkconfig ip6tables off + +cat << EOF > /etc/ntp.conf +$SNIPPET('ntp.conf') +EOF + +## $yum_repo_stanza +## $yum_config_stanza + +$SNIPPET($tool) + +# rm -rf /etc/yum.repos.d/CentOS-Base.repo + + +$SNIPPET('post_anamon') +$SNIPPET('kickstart_done') diff --git a/cobbler/snippets/chef b/cobbler/snippets/chef new file mode 100644 index 0000000..4f194f9 --- /dev/null +++ b/cobbler/snippets/chef @@ -0,0 +1,73 @@ +## Generate client.rb +mkdir -p /etc/chef + + +## Generate validation.pem +cat << EOL > /etc/chef/validation.pem +$SNIPPET('chef-validator.pem') +EOL + +cat << EOL > /etc/chef/client.rb +$SNIPPET('client.rb') +EOL + +## Generate first-boot.json +cat << EOL > /etc/chef/first-boot.json +$SNIPPET('first-boot.json') +EOL + +## Register Server in Rsyslog + + +cat << EOL > /etc/rsyslog.d/chef.conf +$SNIPPET('rsyslogchef') +EOL + +cat << EOL > /etc/rsyslog.conf +$SNIPPET('rsyslogconf') +EOL + +service rsyslog restart + + +cat << EOF > /etc/chef/rerun.sh +#raw +#!/bin/bash +pgrep chef-client +if [ "\$?" != "0" ]; then +chef-client -p /var/run/chef-client.pid -j /etc/chef/first-boot.json &> /tmp/chef.log +fi +#end raw +EOF + + + +## A self-destruct service to boot chef client and register cron job +cat << EOF > /etc/init.d/chef +#!/bin/bash +# chkconfig: 2345 95 20 +# description: Description of the script +# processname: chef-agent +#if $getVar('ntp_server', '') != "" +echo "old date is: \`date\`" 2>&1 > /tmp/ntp.log +echo "path: \$PATH" 2>71 >> /tmp/ntp.log +/sbin/service ntpd stop 2>&1 >> /tmp/ntp.log +/usr/sbin/ntpdate $server 2>&1 >> /tmp/ntp.log +/sbin/service ntpd start 2>&1 >> /tmp/ntp.log +echo "new date is: \`date\`" 2>&1 >> /tmp/ntp.log +#end if + +/etc/chef/rerun.sh + +chmod +x /etc/chef/rerun.sh +crontab -l > mycron +echo "*/1 * * * * /etc/chef/rerun.sh" >> mycron +crontab mycron +rm mycron +chkconfig chef off +rm -rf /etc/init.d/chef +EOF + + +chmod +x /etc/init.d/chef +chkconfig --level 2345 chef on diff --git a/cobbler/snippets/chef-validator.pem b/cobbler/snippets/chef-validator.pem new file mode 120000 index 0000000..340240d --- /dev/null +++ b/cobbler/snippets/chef-validator.pem @@ -0,0 +1 @@ +/etc/chef-server/chef-validator.pem \ No newline at end of file diff --git a/cobbler/snippets/client.rb b/cobbler/snippets/client.rb new file mode 100644 index 0000000..506db72 --- /dev/null +++ b/cobbler/snippets/client.rb @@ -0,0 +1,15 @@ +log_level :info +log_location '/var/log/chef-client.log' +#if $getVar('chef_url', '') != "" +chef_server_url '$chef_url' +#end if +#if $getVar('proxy', '') != "" +http_proxy '$proxy' +https_proxy '$proxy' +#end if +#if $getVar('ignore_proxy', '') != "" +no_proxy '$ignore_proxy' +#end if +validation_client_name 'chef-validator' +# Using default node name (fqdn) + diff --git a/cobbler/snippets/cobbler_register b/cobbler/snippets/cobbler_register new file mode 100644 index 0000000..87dbb37 --- /dev/null +++ b/cobbler/snippets/cobbler_register @@ -0,0 +1,13 @@ +# Begin cobbler registration +#if $getVar('system_name','') == '' +#if $str($getVar('register_new_installs','')) in [ "1", "true", "yes", "y" ] +if [ -f "/usr/bin/cobbler-register" ]; then + cobbler-register --server=$server --fqdn '*AUTO*' --profile=$profile_name --batch +fi +#else +# cobbler registration is disabled in /etc/cobbler/settings +#end if +#else +# skipping for system-based installation +#end if +# End cobbler registration diff --git a/cobbler/snippets/download_config_files b/cobbler/snippets/download_config_files new file mode 100644 index 0000000..0444311 --- /dev/null +++ b/cobbler/snippets/download_config_files @@ -0,0 +1,18 @@ +# Start download cobbler managed config files (if applicable) +#for $tkey, $tpath in $template_files.items() + #set $orig = $tpath + #set $tpath = $tpath.replace("_","__").replace("/","_") + #if $getVar("system_name","") != "" + #set $ttype = "system" + #set $tname = $system_name + #else + #set $ttype = "profile" + #set $tname = $profile_name + #end if + #set $turl = "http://"+$http_server+"/cblr/svc/op/template/"+$ttype+"/"+$tname+"/path/"+$tpath +#if $orig.startswith("/") +mkdir -p `dirname $orig` +wget "$turl" --output-document="$orig" +#end if +#end for +# End download cobbler managed config files (if applicable) diff --git a/cobbler/snippets/download_config_files_deb b/cobbler/snippets/download_config_files_deb new file mode 100644 index 0000000..95297d3 --- /dev/null +++ b/cobbler/snippets/download_config_files_deb @@ -0,0 +1,22 @@ +## Start download cobbler managed config files (if applicable) +#import os +#import stat +#set $cmd = '\\' +#for $tkey, $tpath in $template_files.items() + #set $orig = $tpath + #set $tpath = $tpath.replace("_","__").replace("/","_") + #if $getVar("system_name","") != "" + #set $ttype = "system" + #set $tname = $system_name + #else + #set $ttype = "profile" + #set $tname = $profile_name + #end if + #set $turl = "http://"+$http_server+"/cblr/svc/op/template/"+$ttype+"/"+$tname+"/path/"+$tpath +#if $orig.startswith("/") +#set $perms = oct(stat.S_IMODE(os.stat($tkey).st_mode))[-3:] +#set $cmd = $cmd + "\n" + "mkdir -p " + "`dirname " + $orig + "`; wget -nv " + $turl + " --output-document=" + $orig + "; chmod " + $perms +" " + $orig +"; \\" +#end if +#end for +#echo $cmd +## End download cobbler managed config files (if applicable) diff --git a/cobbler/snippets/first-boot.json b/cobbler/snippets/first-boot.json new file mode 100644 index 0000000..7bd1b9d --- /dev/null +++ b/cobbler/snippets/first-boot.json @@ -0,0 +1,7 @@ +#set run_list=$getVar('$run_list', '') +#set cluster_databag=$getVar('$cluster_databag', None) +#if $cluster_databag != None +{ "run_list": [$run_list], + "cluster": "$cluster_databag" +} +#end if diff --git a/cobbler/snippets/func_install_if_enabled b/cobbler/snippets/func_install_if_enabled new file mode 100644 index 0000000..4bff348 --- /dev/null +++ b/cobbler/snippets/func_install_if_enabled @@ -0,0 +1,4 @@ +#if $str($getVar('func_auto_setup','')) == "1" +func +#end if + diff --git a/cobbler/snippets/func_register_if_enabled b/cobbler/snippets/func_register_if_enabled new file mode 100644 index 0000000..4258fa3 --- /dev/null +++ b/cobbler/snippets/func_register_if_enabled @@ -0,0 +1,26 @@ + +#if $str($getVar('func_auto_setup','')) == "1" +# Start func registration section + +/sbin/chkconfig --level 345 funcd on + +cat < /etc/func/minion.conf +[main] +log_level = INFO +acl_dir = /etc/func/minion-acl.d + +listen_addr = +listen_port = 51234 +EOFM + +cat < /etc/certmaster/minion.conf +[main] +certmaster = $func_master +certmaster_port = 51235 +log_level = DEBUG +cert_dir = /etc/pki/certmaster +EOCM + +# End func registration section +#end if + diff --git a/cobbler/snippets/hosts.xml b/cobbler/snippets/hosts.xml new file mode 100644 index 0000000..e3b578f --- /dev/null +++ b/cobbler/snippets/hosts.xml @@ -0,0 +1,25 @@ + + + + 127.0.0.1 + + localhost + + + #if $getVar("system_name","") != "" + #set $ikeys = $interfaces.keys() + #for $iface in $ikeys + #set $idata = $interfaces[$iface] + #if $idata["interface_type"].lower() in ["","na","bridge","bond"] + + $idata["ip_address"] + + #set $my_interface_hostname_short = $idata["dns_name"].split('.',1)[:1][0] + $idata["dns_name"].lower() $my_interface_hostname_short.lower() + + + #end if + #end for + #end if + + diff --git a/cobbler/snippets/kdump.xml b/cobbler/snippets/kdump.xml new file mode 100644 index 0000000..f03c988 --- /dev/null +++ b/cobbler/snippets/kdump.xml @@ -0,0 +1,35 @@ + + + + true + 256M-2G:64M,2G-:128M + + + + file:///var/crash + true + 64 + 4 + + + compressed + 31 + + + + + + + + + + + + + + + yes + 3 + + + diff --git a/cobbler/snippets/keep_cfengine_keys b/cobbler/snippets/keep_cfengine_keys new file mode 100644 index 0000000..78116ab --- /dev/null +++ b/cobbler/snippets/keep_cfengine_keys @@ -0,0 +1,95 @@ +#raw +# Nifty trick to restore cfengine keys without using a nochroot %post + +echo "Saving cfengine keys..." > /dev/ttyS0 + +SEARCHDIR=/var/cfengine/ppkeys +TEMPDIR=cfengine +PATTERN=localhost + +keys_found=no +# /var could be a separate partition +SHORTDIR=${SEARCHDIR#/var} +if [ $SHORTDIR = $SEARCHDIR ]; then + SHORTDIR='' +fi +insmod /lib/jbd.o +insmod /lib/ext3.o + +mkdir -p /tmp/$TEMPDIR + +function findkeys +{ + for disk in $DISKS; do + name=$(basename $disk) + tmpdir=$(mktemp -d $name.XXXXXX) + mkdir -p /tmp/$tmpdir + mount $disk /tmp/$tmpdir + if [ $? -ne 0 ]; then # Skip to the next partition if the mount fails + rm -rf /tmp/$tmpdir + continue + fi + # Copy current host keys out to be reused + if [ -d /tmp/$tmpdir$SEARCHDIR ] && cp -a /tmp/$tmpdir$SEARCHDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + elif [ -n "$SHORTDIR" ] && [ -d /tmp/$tmpdir$SHORTDIR ] && cp -a /tmp/$tmpdir$SHORTDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + fi + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + done +} + +DISKS=$(awk '{if ($NF ~ "^[a-zA-Z].*[0-9]$" && $NF !~ "c[0-9]+d[0-9]+$" && $NF !~ "^loop.*") print "/dev/"$NF}' /proc/partitions) +# In the awk line above we want to make list of partitions, but not devices/controllers +# cciss raid controllers have partitions like /dev/cciss/cNdMpL, where N,M,L - some digits, we want to make sure 'pL' is there +# No need to scan loopback niether. +# Try to find the keys on ordinary partitions + +findkeys + +# Try software RAID +if [ "$keys_found" = "no" ]; then + if mdadm -As; then + DISKS=$(awk '/md/{print "/dev/"$1}' /proc/mdstat) + findkeys + fi +fi + + +# Try LVM if that didn't work +if [ "$keys_found" = "no" ]; then + lvm lvmdiskscan + vgs=$(lvm vgs | tail -n +2 | awk '{ print $1 }') + for vg in $vgs; do + # Activate any VG we found + lvm vgchange -ay $vg + done + + DISKS=$(lvm lvs | tail -n +2 | awk '{ print "/dev/" $2 "/" $1 }') + findkeys + + # And clean up.. + for vg in $vgs; do + lvm vgchange -an $vg + done +fi + +# Loop until the corresponding rpm is installed +if [ "$keys_found" = "yes" ]; then + while : ; do + sleep 10 + if [ -d /mnt/sysimage$SEARCHDIR ] ; then + cp -af /tmp/$TEMPDIR/${PATTERN}* /mnt/sysimage$SEARCHDIR + logger "keys copied to newly installed system" + break + fi + done & +fi +#end raw diff --git a/cobbler/snippets/keep_files b/cobbler/snippets/keep_files new file mode 100644 index 0000000..858db5d --- /dev/null +++ b/cobbler/snippets/keep_files @@ -0,0 +1,154 @@ +## This snippet preserves files during re-build. +## It supersedes other similar snippets - keep_*_keys. +## Put it in %pre section of the kickstart template file +## It uses preserve_files field which should contain a list of items to preserve +## This field for now could contain any of the following: +## 'ssh', 'cfengine', 'rhn' in any order +## 'rhn' part of this snippet should NOT be used with systems subscribed +## to Red Hat Satellite Server or Spacewalk as these +## have a concept of "reactivation keys" to keep the systems +## appearing to be the same. Also do not use if changing +## base channels, i.e. RHEL4 -> RHEL5 upgrades. +## + +#if $getVar('$preserve_files','') != '' + #set $preserve_files = $getVar('$preserve_files','') + preserve_files = $preserve_files + +#raw +# Nifty trick to restore keys without using a nochroot %post + +echo "Saving keys..." > /dev/ttyS0 + +insmod /lib/jbd.o +insmod /lib/ext3.o + +function findkeys +{ + for disk in $DISKS; do + name=$(basename $disk) + tmpdir=$(mktemp -d $name.XXXXXX) + mkdir -p /tmp/$tmpdir + mount $disk /tmp/$tmpdir + if [ $? -ne 0 ]; then # Skip to the next partition if the mount fails + rm -rf /tmp/$tmpdir + continue + fi + # Copy current host keys out to be reused + if [ -d /tmp/$tmpdir$SEARCHDIR ] && cp -a /tmp/$tmpdir$SEARCHDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + elif [ -n "$SHORTDIR" ] && [ -d /tmp/$tmpdir$SHORTDIR ] && cp -a /tmp/$tmpdir$SHORTDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + fi + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + done +} + +function search_for_keys +{ + + SEARCHDIR=$1 + TEMPDIR=$2 + PATTERN=$3 + + keys_found=no + # /var could be a separate partition + SHORTDIR=${SEARCHDIR#/var} + if [ $SHORTDIR = $SEARCHDIR ]; then + SHORTDIR='' + fi + + mkdir -p /tmp/$TEMPDIR + + DISKS=$(awk '{if ($NF ~ "^[a-zA-Z].*[0-9]$" && $NF !~ "c[0-9]+d[0-9]+$" && $NF !~ "^loop.*") print "/dev/"$NF}' /proc/partitions) + # In the awk line above we want to make list of partitions, but not devices/controllers + # cciss raid controllers have partitions like /dev/cciss/cNdMpL, where N,M,L - some digits, we want to make sure 'pL' is there + # No need to scan loopback niether. + # Try to find the keys on ordinary partitions + + findkeys + + # Try software RAID + if [ "$keys_found" = "no" ]; then + if mdadm -As; then + DISKS=$(awk '/md/{print "/dev/"$1}' /proc/mdstat) + findkeys + fi + fi + + + # Try LVM if that didn't work + if [ "$keys_found" = "no" ]; then + lvm lvmdiskscan + vgs=$(lvm vgs | tail -n +2 | awk '{ print $1 }') + for vg in $vgs; do + # Activate any VG we found + lvm vgchange -ay $vg + done + + DISKS=$(lvm lvs | tail -n +2 | awk '{ print "/dev/" $2 "/" $1 }') + findkeys + + # And clean up.. + for vg in $vgs; do + lvm vgchange -an $vg + done + fi +} + +function restore_keys +{ + SEARCHDIR=$1 + TEMPDIR=$2 + PATTERN=$3 + # Loop until the corresponding rpm is installed if the keys are saved + if [ "$keys_found" = "yes" ] && [ -f /tmp/$TEMPDIR/${PATTERN}* ]; then + while : ; do + sleep 10 + if [ -d /mnt/sysimage$SEARCHDIR ] ; then + cp -af /tmp/$TEMPDIR/${PATTERN}* /mnt/sysimage$SEARCHDIR + logger "$TEMPDIR keys copied to newly installed system" + break + fi + done & + fi +} + +for key in $preserve_files +do + if [ $key = 'ssh' ]; then + search_for_keys '/etc/ssh' 'ssh' 'ssh_host_' + elif [ $key = 'cfengine' ]; then + search_for_keys '/var/cfengine/ppkeys' 'cfengine' 'localhost' + elif [ $key = 'rhn' ]; then + search_for_keys '/etc/sysconfig/rhn', 'rhn', '*' + else + echo "No keys to save!" > /dev/ttyS0 + fi +done + +# now restore keys if found + +for key in $preserve_files +do + if [ $key = 'ssh' ]; then + restore_keys '/etc/ssh' 'ssh' 'ssh_host_' + elif [ $key = 'cfengine' ]; then + restore_keys '/var/cfengine/ppkeys' 'cfengine' 'localhost' + elif [ $key = 'rhn' ]; then + restore_keys '/etc/sysconfig/rhn', 'rhn', '*' + else + echo "Nothing to restore!" > /dev/ttyS0 + fi +done + + +#end raw +#end if diff --git a/cobbler/snippets/keep_rhn_keys b/cobbler/snippets/keep_rhn_keys new file mode 100644 index 0000000..59bfc5d --- /dev/null +++ b/cobbler/snippets/keep_rhn_keys @@ -0,0 +1,88 @@ +#raw + +## this snippet should NOT be used with systems subscribed +## to Red Hat Satellite Server or Spacewalk as these +## have a concept of "reactivation keys" to keep the systems +## appearing to be the same. Also do not use if changing +## base channels, i.e. RHEL4 -> RHEL5 upgrades. + +echo "Saving RHN keys..." > /dev/ttyS0 + +rhn_keys_found=no + +insmod /lib/jbd.o +insmod /lib/ext3.o + +mkdir -p /tmp/rhn + +drives=$(list-harddrives | awk '{print $1}') +for disk in $drives; do + DISKS="$DISKS $(fdisk -l /dev/$disk | awk '/^\/dev/{print $1}')" +done + +# Try to find the keys on ordinary partitions +for disk in $DISKS; do + name=$(basename $disk) + mkdir -p /tmp/$name + mount $disk /tmp/$name + [ $? -eq 0 ] || continue # Skip to the next partition if the mount fails + + # Copy current RHN host keys out to be reused + if [ -d /tmp/${name}/etc/sysconfig/rhn ]; then + cp -a /tmp/${name}/etc/sysconfig/rhn/install-num /tmp/rhn + cp -a /tmp/${name}/etc/sysconfig/rhn/systemid /tmp/rhn + cp -a /tmp/${name}/etc/sysconfig/rhn/up2date /tmp/rhn + rhn_keys_found="yes" + umount /tmp/$name + break + fi + umount /tmp/$name + rm -r /tmp/$name +done + +# Try LVM if that didn't work +if [ "$rhn_keys_found" = "no" ]; then + lvm lvmdiskscan + vgs=$(lvm vgs | tail -n +2 | awk '{ print $1 }') + for vg in $vgs; do + # Activate any VG we found + lvm vgchange -ay $vg + done + + lvs=$(lvm lvs | tail -n +2 | awk '{ print "/dev/" $2 "/" $1 }') + for lv in $lvs; do + tmpdir=$(mktemp -d findkeys.XXXXXX) + mkdir -p /tmp/${tmpdir} + mount $lv /tmp/${tmpdir} || continue # Skip to next volume if this fails + + # Let's see if the keys are in there + if [ -d /tmp/${tmpdir}/etc/sysconfig/rhn ]; then + cp -a /tmp/${tmpdir}/etc/sysconfig/rhn/install-num* /tmp/rhn/ + cp -a /tmp/${tmpdir}/etc/sysconfig/rhn/systemid* /tmp/rhn/ + cp -a /tmp/${tmpdir}/etc/sysconfig/rhn/up2date /tmp/rhn/ + rhn_keys_found="yes" + umount /tmp/${tmpdir} + break # We're done! + fi + umount /tmp/${tmpdir} + rm -r /tmp/${tmpdir} + done + + # And clean up.. + for vg in $vgs; do + lvm vgchange -an $vg + done +fi + +# Loop until the RHN rpm is installed +if [ "$rhn_keys_found" = "yes" ]; then + while : ; do + sleep 10 + if [ -d /mnt/sysimage/etc/sysconfig/rhn ] ; then + cp -af /tmp/rhn/* /mnt/sysimage/etc/sysconfig/rhn/ + logger "RHN KEY copied to newly installed system" + break + fi + done & +fi +#end raw diff --git a/cobbler/snippets/keep_ssh_host_keys b/cobbler/snippets/keep_ssh_host_keys new file mode 100644 index 0000000..2c01c69 --- /dev/null +++ b/cobbler/snippets/keep_ssh_host_keys @@ -0,0 +1,114 @@ +#raw +# Nifty trick to restore keys without using a nochroot %post + +echo "Saving keys..." > /dev/ttyS0 + +SEARCHDIR=/etc/ssh +TEMPDIR=ssh +PATTERN=ssh_host_ + +keys_found=no +# /var could be a separate partition +SHORTDIR=${SEARCHDIR#/var} +if [ $SHORTDIR = $SEARCHDIR ]; then + SHORTDIR='' +fi +insmod /lib/jbd.o +insmod /lib/ext3.o + +mkdir -p /tmp/$TEMPDIR + + +function findkeys +{ + for disk in $DISKS; do + name=$(basename $disk) + tmpdir=$(mktemp -d $name.XXXXXX) + mkdir -p /tmp/$tmpdir + mount $disk /tmp/$tmpdir + if [ $? -ne 0 ]; then # Skip to the next partition if the mount fails + rm -rf /tmp/$tmpdir + continue + fi + # Copy current host keys out to be reused + if [ -d /tmp/$tmpdir$SEARCHDIR ] && cp -a /tmp/$tmpdir$SEARCHDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + elif [ -n "$SHORTDIR" ] && [ -d /tmp/$tmpdir$SHORTDIR ] && cp -a /tmp/$tmpdir$SHORTDIR/${PATTERN}* /tmp/$TEMPDIR; then + keys_found="yes" + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + break + fi + umount /tmp/$tmpdir + rm -r /tmp/$tmpdir + done +} + +DISKS=$(awk '{if ($NF ~ "^[a-zA-Z].*[0-9]$" && $NF !~ "c[0-9]+d[0-9]+$" && $NF !~ "^loop.*") print "/dev/"$NF}' /proc/partitions) +# In the awk line above we want to make list of partitions, but not devices/controllers +# cciss raid controllers have partitions like /dev/cciss/cNdMpL, where N,M,L - some digits, we want to make sure 'pL' is there +# No need to scan loopback niether. +# Try to find the keys on ordinary partitions + +findkeys + +# Try software RAID +if [ "$keys_found" = "no" ]; then + if mdadm -As; then + DISKS=$(awk '/md/{print "/dev/"$1}' /proc/mdstat) + findkeys + # unmount and deactivate all md + for md in $DISKS ; do + umount $md + mdadm -S $md + done + fi +fi + + +# Try LVM if that didn't work +if [ "$keys_found" = "no" ]; then + lvm lvmdiskscan + vgs=$(lvm vgs | tail -n +2 | awk '{ print $1 }') + for vg in $vgs; do + # Activate any VG we found + lvm vgchange -ay $vg + done + + DISKS=$(lvm lvs | tail -n +2 | awk '{ print "/dev/" $2 "/" $1 }') + findkeys + + # And clean up.. + for vg in $vgs; do + lvm vgchange -an $vg + done +fi + +# Loop until the corresponding rpm is installed +if [ "$keys_found" = "yes" ]; then + if [ "$PATTERN" = "ssh_host_" ]; then + while : ; do + sleep 10 + if [ -f /etc/ssh/ssh_host_key ] ; then + cp -af /tmp/$TEMPDIR/${PATTERN}* $SEARCHDIR + break + fi + done 1>/dev/null 2>/dev/null & + fi + while : ; do + sleep 10 + if [ -d /mnt/sysimage$SEARCHDIR ] ; then + cp -af /tmp/$TEMPDIR/${PATTERN}* /mnt/sysimage$SEARCHDIR + if [ -e "/sbin/restorecon"]; then + /sbin/restorecon -r /etc/ssh + fi + logger "keys copied to newly installed system" + break + fi + done 1>/dev/null 2>/dev/null & +fi +#end raw + diff --git a/cobbler/snippets/kickstart_done b/cobbler/snippets/kickstart_done new file mode 100644 index 0000000..c756695 --- /dev/null +++ b/cobbler/snippets/kickstart_done @@ -0,0 +1,80 @@ +#set system_name = $getVar('system_name','') +#set profile_name = $getVar('profile_name','') +#set breed = $getVar('breed','') +#set os_version = $getVar('os_version','') +#set srv = $getVar('http_server','') +#set kickstart = $getVar('kickstart','') +#set run_install_triggers = $str($getVar('run_install_triggers','')) +#set pxe_just_once = $str($getVar('pxe_just_once','')) +#set nopxe = "" +#set saveks = "" +#set runpost = "" +#if $system_name != '' + ## PXE JUST ONCE + #if $pxe_just_once in [ "1", "true", "yes", "y" ] + #if $breed == 'redhat' + #set nopxe = "\nwget \"http://%s/cblr/svc/op/nopxe/system/%s\" -O /var/log/nopxe" % (srv, system_name) + #else if $breed == 'vmware' and $os_version == 'esx4' + #set nopxe = "\ncurl \"http://%s/cblr/svc/op/nopxe/system/%s\" -o /var/log/nopxe" % (srv, system_name) + #else if $breed == 'vmware' + #set nopxe = "\nwget \"http://%s/cblr/svc/op/nopxe/system/%s\" -O /var/log/nopxe" % (srv, system_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set nopxe = "\nwget \"http://%s/cblr/svc/op/nopxe/system/%s\" -O /var/log/nopxe" % (srv, system_name) + #else + ## default to wget + #set nopxe = "wget \"http://%s/cblr/svc/op/nopxe/system/%s\" -O /var/log/nopxe;" % (srv, system_name) + #end if + #end if + ## SAVE KICKSTART + #if $kickstart != '' + #if $breed == 'redhat' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /root/cobbler.ks" % (srv, "system", system_name) + #else if $breed == 'vmware' and $os_version == 'esx4' + #set saveks = "\ncurl \"http://%s/cblr/svc/op/ks/%s/%s\" -o /root/cobbler.ks" % (srv, "system", system_name) + #else if $breed == 'vmware' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /var/log/cobbler.ks" % (srv, "system", system_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /var/log/cobbler.seed" % (srv, "system", system_name) + #end if + #end if + ## RUN POST TRIGGER + #if $run_install_triggers in [ "1", "true", "yes", "y" ] + #if $breed == 'redhat' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #else if $breed == 'vmware' and $os_version == 'esx4' + #set runpost = "\ncurl \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -o /dev/null" % (srv, "system", system_name) + #else if $breed == 'vmware' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #end if + #end if +#else if $profile_name != '' + ## SAVE KICKSTART + #if $kickstart != '' + #if $breed == 'redhat' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /root/cobbler.ks" % (srv, "profile", profile_name) + #else if $breed == 'vmware' and $os_version == 'esx4' + #set saveks = "\ncurl \"http://%s/cblr/svc/op/ks/%s/%s\" -o /root/cobbler.ks" % (srv, "profile", profile_name) + #else if $breed == 'vmware' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /var/log/cobbler.ks" % (srv, "profile", profile_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set saveks = "\nwget \"http://%s/cblr/svc/op/ks/%s/%s\" -O /var/log/cobbler.seed" % (srv, "profile", profile_name) + #end if + #end if + ## RUN POST TRIGGER + #if $run_install_triggers in [ "1", "true", "yes", "y" ] + #if $breed == 'redhat' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "profile", profile_name) + #else if $breed == 'vmware' and $os_version == 'esx4' + #set runpost = "\ncurl \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -o /dev/null" % (srv, "profile", profile_name) + #else if $breed == 'vmware' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "profile", profile_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, "profile", profile_name) + #end if + #end if +#end if +#echo $saveks +#echo $runpost +#echo $nopxe diff --git a/cobbler/snippets/kickstart_start b/cobbler/snippets/kickstart_start new file mode 100644 index 0000000..13705c8 --- /dev/null +++ b/cobbler/snippets/kickstart_start @@ -0,0 +1,30 @@ +#set system_name = $getVar('system_name','') +#set profile_name = $getVar('profile_name','') +#set breed = $getVar('breed','') +#set srv = $getVar('http_server','') +#set run_install_triggers = $str($getVar('run_install_triggers','')) +#set runpre = "" +#if $system_name != '' + ## RUN PRE TRIGGER + #if $run_install_triggers in [ "1", "true", "yes", "y" ] + #if $breed == 'redhat' + #set runpre = "\nwget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #else if $breed == 'vmware' + #set runpre = "\nwget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #else if $breed == 'debian' or $breed == 'ubuntu' + #set runpre = "wget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #else if $breed == 'vmware' + #set runpre = "wget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "system", system_name) + #end if + #end if +#else if $profile_name != '' + ## RUN PRE TRIGGER + #if $run_install_triggers in [ "1", "true", "yes", "y" ] + #if $breed == 'redhat' + #set runpre = "\nwget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "profile", profile_name) + #else if $breed == 'vmware' + #set runpre = "\nwget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, "profile", profile_name) + #end if + #end if +#end if +#echo $runpre diff --git a/cobbler/snippets/koan_environment b/cobbler/snippets/koan_environment new file mode 100644 index 0000000..3ad417f --- /dev/null +++ b/cobbler/snippets/koan_environment @@ -0,0 +1,4 @@ +# Start koan environment setup +echo "export COBBLER_SERVER=$server" > /etc/profile.d/cobbler.sh +echo "setenv COBBLER_SERVER $server" > /etc/profile.d/cobbler.csh +# End koan environment setup diff --git a/cobbler/snippets/late_apt_repo_config b/cobbler/snippets/late_apt_repo_config new file mode 100644 index 0000000..8e79bd3 --- /dev/null +++ b/cobbler/snippets/late_apt_repo_config @@ -0,0 +1,24 @@ +# start late_apt_repo_config +cat</etc/apt/sources.list +deb http://$http_server/cblr/links/$distro_name $os_version main +#set $repo_data = $getVar("repo_data",[]) +#for $repo in $repo_data + #for $dist in $repo.apt_dists + #set $comps = " ".join($repo.apt_components) + #if $repo.comment != "" +# ${repo.comment} + #end if + #if $repo.arch == "x86_64" + #set $rarch = "[arch=amd64]" + #else + #set $rarch = "[arch=%s]" % $repo.arch + #end if + #if $repo.mirror_locally +deb ${rarch} http://$http_server/cblr/repo_mirror/${repo.name} $dist $comps + #else +deb ${rarch} ${repo.mirror} $dist $comps + #end if + #end for +#end for +EOF +# end late_apt_repo_config diff --git a/cobbler/snippets/log_ks_post b/cobbler/snippets/log_ks_post new file mode 100644 index 0000000..d1df026 --- /dev/null +++ b/cobbler/snippets/log_ks_post @@ -0,0 +1,2 @@ +set -x -v +exec 1>/root/ks-post.log 2>&1 diff --git a/cobbler/snippets/log_ks_pre b/cobbler/snippets/log_ks_pre new file mode 100644 index 0000000..fe71c59 --- /dev/null +++ b/cobbler/snippets/log_ks_pre @@ -0,0 +1,12 @@ +set -x -v +exec 1>/tmp/ks-pre.log 2>&1 + +# Once root's homedir is there, copy over the log. +while : ; do + sleep 10 + if [ -d /mnt/sysimage/root ]; then + cp /tmp/ks-pre.log /mnt/sysimage/root/ + logger "Copied %pre section log to system" + break + fi +done & diff --git a/cobbler/snippets/main_partition_select b/cobbler/snippets/main_partition_select new file mode 100644 index 0000000..9d996e6 --- /dev/null +++ b/cobbler/snippets/main_partition_select @@ -0,0 +1,3 @@ +# partition selection +%include /tmp/partinfo + diff --git a/cobbler/snippets/network_config b/cobbler/snippets/network_config new file mode 100644 index 0000000..6de06e5 --- /dev/null +++ b/cobbler/snippets/network_config @@ -0,0 +1,7 @@ +## start of cobbler network_config generated code +#if $getVar("system_name","") != "" +# Using "new" style networking config, by matching networking information to the physical interface's +# MAC-address +%include /tmp/pre_install_network_config +#end if +## end of cobbler network_config generated code diff --git a/cobbler/snippets/network_config_esx b/cobbler/snippets/network_config_esx new file mode 100644 index 0000000..e378569 --- /dev/null +++ b/cobbler/snippets/network_config_esx @@ -0,0 +1,47 @@ +#import re + +#if $getVar("system_name","") != "" + #set ikeys = $interfaces.keys() + #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") + #for $iname in $ikeys + #set $idata = $interfaces[$iname] + #set $mac = $idata["mac_address"] + #set $static = $idata["static"] + #set $ip = $idata["ip_address"] + #set $netmask = $idata["netmask"] + #set $type = $idata["interface_type"] + #if $vlanpattern.match($iname) or $type in ("master","bond","bridge") + ## If this is a VLAN interface, skip it, anaconda doesn't know + ## about VLANs. + #set $is_vlan = "true" + #else + #set $is_vlan = "false" + #end if + #if $mac != "" or $ip != "" and $is_vlan == "false" + #if $static == True: + #set $network_str = "--bootproto=static" + #if $ip != "": + #set $network_str = $network_str + " --ip=" + $ip + #if $netmask != "": + #set $network_str = $network_str + " --netmask=" + $netmask + #end if + #if $gateway != "": + #set $network_str = $network_str + " --gateway=" + $gateway + #end if + #if $name_servers and $name_servers[0] != "": + ## Anaconda only allows one nameserver + #set $network_str = $network_str + " --nameserver=" + $name_servers[0] + #end if + #end if + #else + #set $network_str = "--bootproto=dhcp" + #end if + #if $hostname != "" + #set $network_str = $network_str + " --hostname=" + $hostname + #end if + #else + #set $network_str = "--bootproto=dhcp" + #end if +network $network_str --device=$mac + #end for +#end if diff --git a/cobbler/snippets/network_config_esxi b/cobbler/snippets/network_config_esxi new file mode 100644 index 0000000..ad29080 --- /dev/null +++ b/cobbler/snippets/network_config_esxi @@ -0,0 +1,56 @@ +#import re + +#if $getVar("system_name","") != "" + #set ikeys = $interfaces.keys() + #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") + #for $iname in $ikeys + #set $idata = $interfaces[$iname] + #set $mac = $idata["mac_address"] + #set $static = $idata["static"] + #set $ip = $idata["ip_address"] + #set $netmask = $idata["netmask"] + #set $type = $idata["interface_type"] + #set $vlanid = "" + #if $vlanpattern.match($iname) or $type in ("master","bond","bridge") + ## If this is a VLAN interface, skip it, anaconda doesn't know + ## about VLANs. + #set $is_vlan = "true" + #set $vlanid = " --vlanid=" + $iname.split('.')[1] + #set $iname = $iname.split('.')[0] + #else + #set $is_vlan = "false" + #end if + #if $mac != "" or $ip != "" and $is_vlan == "false" + #if $static == True: + #set $network_str = "--bootproto=static" + #if $ip != "": + #set $network_str = $network_str + " --ip=" + $ip + #if $netmask != "": + #set $network_str = $network_str + " --netmask=" + $netmask + #end if + #if $gateway != "": + #set $network_str = $network_str + " --gateway=" + $gateway + #end if + #if $name_servers and $name_servers[0] != "": + #set $network_str = $network_str + " --nameserver=" + $name_servers[0] + #if len($name_servers) > 1 and $name_servers[1] != "": + #set $network_str += "," + $name_servers[1] + #end if + #end if + #end if + #else + #set $network_str = "--bootproto=dhcp" + #end if + #if $hostname != "" + #set $network_str = $network_str + " --hostname=" + $hostname + #end if + #else + #set $network_str = "--bootproto=dhcp" + + #end if + #if $vlanid != "" + #set $network_str = $network_str + $vlanid + #end if +network $network_str --device=$iname + #end for +#end if diff --git a/cobbler/snippets/networking.xml b/cobbler/snippets/networking.xml new file mode 100644 index 0000000..2290d9f --- /dev/null +++ b/cobbler/snippets/networking.xml @@ -0,0 +1,131 @@ +#set $hostname = $getVar("hostname","") +#if $hostname == "" +#set $hostname = $getVar("system_name","cobbler") +#end if +#if $getVar("dns_name_eth0","") != "" + #set $my_hostname = $hostname.split('.',1)[:1][0] + #set $my_domainname = $dns_name_eth0.split('.',1)[1:][0] +#else + #set $my_hostname = $hostname + #set $my_domainname = "site" +#end if + + + false + + + + + + false + false + false + + $my_hostname + $my_domainname + #if $getVar("name_servers_search","") != "" + + #for $sd in $name_servers_search + $sd + #end for + + #end if + + #for $ns in $name_servers + $ns + #end for + + + + #if $getVar("system_name","") != "" + #set $ikeys = $interfaces.keys() + #for $iface in $ikeys + #set $idata = $interfaces[$iface] + #set $mac = $idata["mac_address"] + #set $ip = $idata["ip_address"] + #set $netmask = $idata["netmask"] + #set $iface_type = $idata["interface_type"] + #set $bonding_opts = $idata["bonding_opts"] + #if $iface_type.lower() == "bond" + + yes + $bonding_opts.lower() + #set $loop_ikeys = $interfaces.keys() + #set $loop_counter = 0 + #for $loop_iface in $loop_ikeys + #set $loop_idata = $interfaces[$loop_iface] + #set $loop_interface_type = $loop_idata["interface_type"] + #if $loop_interface_type.lower == "bond_slave" + #if $loop_idata["interface_master"] != "" + #if $loop_idata["interface_master"].lower() == $iface.lower() + $loop_iface + #set $loop_counter += 1 + #end if + #end if + #end if + #end for + static + $iface + $ip + $netmask + auto + no + + #end if + #if $iface_type.lower() in ["bond_slave","bridge_slave"] + + none + $iface + off + no + + #end if + #if $iface_type.lower() in ["","na"] + + static + $iface + $mac.lower() + $ip + $netmask + auto + no + + #end if + #end for + #end if + + false + + #if $getVar("system_name","") != "" + #set $ikeys = $interfaces.keys() + #for $iface in $ikeys + #set $idata = $interfaces[$iface] + #set $mac = $idata["mac_address"] + #set $interface_type = $idata["interface_type"] + #if $mac.lower() != "" + #if $interface_type.lower() not in ["bond","bridge"] + + $iface + ATTR{address} + $mac.lower() + + #end if + #end if + #end for + #end if + + + false + #if $getVar("system_name","") != "" + ## TODO: add in static routes here + + + default + - + - + $gateway + + + #end if + + diff --git a/cobbler/snippets/ntp.conf b/cobbler/snippets/ntp.conf new file mode 100644 index 0000000..d27eb6e --- /dev/null +++ b/cobbler/snippets/ntp.conf @@ -0,0 +1,63 @@ +# For more information about this file, see the man pages +# ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5). + +driftfile /var/lib/ntp/drift + +# Permit time synchronization with our time source, but do not +# permit the source to query or modify the service on this system. +restrict default kod nomodify notrap nopeer noquery +restrict -6 default kod nomodify notrap nopeer noquery + +# Permit all access over the loopback interface. This could +# be tightened as well, but to do so would effect some of +# the administrative functions. +restrict 127.0.0.1 +restrict -6 ::1 + +# Hosts on local network are less restricted. +# restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap + +# Use public servers from the pool.ntp.org project. +# Please consider joining the pool (http://www.pool.ntp.org/join.html). +# server 0.centos.pool.ntp.org +# server 1.centos.pool.ntp.org +# server 2.centos.pool.ntp.org +#if $getVar('ntp_server', '') != "" +server $ntp_server +#end if + +# broadcast 192.168.1.255 autokey # broadcast server +# broadcastclient # broadcast client +# broadcast 224.0.1.1 autokey # multicast server +# multicastclient 224.0.1.1 # multicast client +# manycastserver 239.255.254.254 # manycast server +# manycastclient 239.255.254.254 autokey # manycast client + +# Undisciplined Local Clock. This is a fake driver intended for backup +# and when no outside source of synchronized time is available. +server 127.127.1.0 # local clock +fudge 127.127.1.0 stratum 10 + +# Enable public key cryptography. +# crypto + +includefile /etc/ntp/crypto/pw + +# Key file containing the keys and key identifiers used when operating +# with symmetric key cryptography. +keys /etc/ntp/keys + +# Specify the key identifiers which are trusted. +# trustedkey 4 8 42 + +# Specify the key identifier to use with the ntpdc utility. +# requestkey 8 + +# Specify the key identifier to use with the ntpq utility. +# controlkey 8 + +# Enable writing of statistics records. +# statistics clockstats cryptostats loopstats peerstats + +# make ntpdate using the server in conf to update the system time. +NTPDATE_USE_NTP_CONF=yes diff --git a/cobbler/snippets/partition_disks b/cobbler/snippets/partition_disks new file mode 100644 index 0000000..e2ea9aa --- /dev/null +++ b/cobbler/snippets/partition_disks @@ -0,0 +1,46 @@ +#set hostname=$getVar('$hostname',None) +#set partition = $getVar('$partition', None) + +#if $hostname == None +#set $vgname = "VolGroup00" +#else +#set $vgname = $hostname.split('.')[0] +#end if + +set \$(list-harddrives) +let numd=\$#/2 +d1=\$1 +d2=\$3 + +echo "clearpart --all --initlabel" > /tmp/part-include +echo "part /boot --fstype ext3 --size=100 --ondisk=\$d1 --asprimary" >> /tmp/part-include +echo "part swap --recommended --ondisk=\$d1" >> /tmp/part-include +echo "part pv.01 --size=1 --grow --ondisk=\$d1" >> /tmp/part-include +# if [ "$numd" == "2" ] +# echo "part pv.02 --size=1 --glow --ondisk=\$d2" >> /tmp/part-include +# echo "volgroup $vgname pv.01 pv.02" >> /tmp/part-include +# else + echo "volgroup $vgname pv.01" >> /tmp/part-include +# fi + +echo "logvol / --fstype ext3 --vgname=$vgname --size=1 --grow --percent=30 --name=rootvol" >> /tmp/part-include + +#if $partition != None + #set vol_sizes = [part.strip() for part in $partition.split(';') if part.strip()] + #for vol_and_size in vol_sizes + #set vol, vol_size = $vol_and_size.split(' ', 1) + #set vol = $vol.strip() + #set vol_size = $vol_size.strip() + #if $vol.startswith('/') + #set volname = $vol[1:] + #if $vol_size.endswith('%'): + #set vol_percent = vol_size[:-1] +echo "logvol $vol --fstype ext3 --vgname=$vgname --size=1 --grow --percent=$vol_percent --name=${volname}vol" >> /tmp/part-include + #else +echo "logvol $vol --vgname=$vgname --fstype ext3 --size=$vol_size --name=${volname}vol" >> /tmp/part-include + #end if + #else +# $vol is not starts with / + #end if + #end for +#end if diff --git a/cobbler/snippets/partition_select b/cobbler/snippets/partition_select new file mode 100644 index 0000000..40c6e47 --- /dev/null +++ b/cobbler/snippets/partition_select @@ -0,0 +1,34 @@ +%include /tmp/partinfo + +%pre +# Determine how many drives we have +set \$(list-harddrives) +let numd=\$#/2 +d1=\$1 +d2=\$3 + +# Determine architecture-specific partitioning needs +EFI_PART="" +PPC_PREP_PART="" +BOOT_PART="" + +case \$(uname -m) in + ia64) + EFI_PART="part /boot/efi --fstype vfat --size 200 --recommended" + ;; + ppc*) + PPC_PREP_PART="part None --fstype 'PPC PReP Boot' --size 8" + BOOT_PART="part /boot --fstype ext3 --size 200 --recommended" + ;; + *) + BOOT_PART="part /boot --fstype ext3 --size 200 --recommended" + ;; +esac + +cat << EOF > /tmp/partinfo +\$EFI_PART +\$PPC_PREP_PART +\$BOOT_PART +part / --fstype ext3 --size=1024 --grow --ondisk=\$d1 --asprimary +part swap --recommended --ondisk=\$d1 --asprimary +EOF diff --git a/cobbler/snippets/post_anamon b/cobbler/snippets/post_anamon new file mode 100644 index 0000000..d0d5713 --- /dev/null +++ b/cobbler/snippets/post_anamon @@ -0,0 +1,23 @@ +#if $str($getVar('anamon_enabled','')) == "1" + +## install anamon script +wget -O /usr/local/sbin/anamon "http://$server:$http_port/cobbler/aux/anamon" +## install anamon system service +wget -O /etc/rc.d/init.d/anamon "http://$server:$http_port/cobbler/aux/anamon.init" + +## adjust permissions +chmod 755 /etc/rc.d/init.d/anamon /usr/local/sbin/anamon +test -d /selinux && restorecon /etc/rc.d/init.d/anamon /usr/local/sbin/anamon + +## enable the script +chkconfig --add anamon + +## configure anamon service +cat << __EOT__ > /etc/sysconfig/anamon +COBBLER_SERVER="$server" +COBBLER_PORT="$http_port" +COBBLER_NAME="$name" +LOGFILES="/var/log/boot.log /var/log/messages /var/log/dmesg" +__EOT__ + +#end if diff --git a/cobbler/snippets/post_install_kernel_options b/cobbler/snippets/post_install_kernel_options new file mode 100644 index 0000000..1cff9c9 --- /dev/null +++ b/cobbler/snippets/post_install_kernel_options @@ -0,0 +1,14 @@ +#if $getVar('kernel_options_post','') != '' +# Start post install kernel options update +if [ -f /etc/default/grub ]; then + TMP_GRUB=\$(gawk 'match(\$0,/^GRUB_CMDLINE_LINUX="([^"]+)"/,a) {printf("%s\n",a[1])}' /etc/default/grub) + sed -i '/^GRUB_CMDLINE_LINUX=/d' /etc/default/grub + echo "GRUB_CMDLINE_LINUX=\"\$TMP_GRUB $kernel_options_post\"" >> /etc/default/grub + grub2-mkconfig -o /boot/grub2/grub.cfg +else + /sbin/grubby --update-kernel=\$(/sbin/grubby --default-kernel) --args="$kernel_options_post" +fi +# End post install kernel options update +#end if + + diff --git a/cobbler/snippets/post_install_network_config b/cobbler/snippets/post_install_network_config new file mode 100644 index 0000000..21c7202 --- /dev/null +++ b/cobbler/snippets/post_install_network_config @@ -0,0 +1,345 @@ +# Start post_install_network_config generated code +#if $getVar('promisc_nics', '') != "" + #set promisc_interfaces = [promisc.strip() for promisc in $promisc_nics.split(',') if promisc.strip()] +#else + #set promisc_interfaces = [] +#end if +#if $getVar("system_name","") != "" + ## this is being provisioned by system records, not profile records + ## so we can do the more complex stuff + ## get the list of interface names + #set ikeys = $interfaces.keys() + #set osversion = $getVar("os_version","") + #import re + #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") + ## Determine if we should use the MAC address to configure the interfaces first + ## Only physical interfaces are required to have a MAC address + ## Also determine the number of bonding devices we have, so we can set the + ## max-bonds option in modprobe.conf accordingly. -- jcapel + #set $configbymac = True + #set $numbondingdevs = 0 + #set $enableipv6 = False + ## ============================================================================= + #for $iname in $ikeys + ## look at the interface hash data for the specific interface + #set $idata = $interfaces[$iname] + ## do not configure by mac address if we don't have one AND it's not for bonding/vlans + ## as opposed to a "real" physical interface + #if $idata.get("mac_address", "") == "" and not $vlanpattern.match($iname) and not $idata.get("interface_type", "").lower() in ("master","bond","bridge"): + ## we have to globally turn off the config by mac feature as we can't + ## use it now + #set $configbymac = False + #end if + ## count the number of bonding devices we have. + #if $idata.get("interface_type", "").lower() in ("master","bond","bonded_bridge_slave") + #set $numbondingdevs += 1 + #end if + ## enable IPv6 networking if we set an ipv6 address or turn on autoconfiguration + #if $idata.get("ipv6_address", "") != "" or $ipv6_autoconfiguration == True + #set $enableipv6 = True + #end if + #end for + ## end looping through the interfaces to see which ones we need to configure. + ## ============================================================================= + #set $i = 0 + ## setup bonding if we have to + #if $numbondingdevs > 0 + +# we have bonded interfaces, so set max_bonds +if [ -f "/etc/modprobe.conf" ]; then + echo "options bonding max_bonds=$numbondingdevs" >> /etc/modprobe.conf +fi + #end if + ## ============================================================================= + ## create a staging directory to build out our network scripts into + ## make sure we preserve the loopback device + +# create a working directory for interface scripts +mkdir /etc/sysconfig/network-scripts/cobbler +cp /etc/sysconfig/network-scripts/ifcfg-lo /etc/sysconfig/network-scripts/cobbler/ + ## ============================================================================= + ## configure the gateway if set up (this is global, not a per-interface setting) + #if $gateway != "" + +# set the gateway in the network configuration file +grep -v GATEWAY /etc/sysconfig/network > /etc/sysconfig/network.cobbler +echo "GATEWAY=$gateway" >> /etc/sysconfig/network.cobbler +rm -f /etc/sysconfig/network +mv /etc/sysconfig/network.cobbler /etc/sysconfig/network + #end if + ## ============================================================================= + ## Configure the system's primary hostname. This is also passed to anaconda, but + ## anaconda doesn't seem to honour it in DHCP-setups. + #if $hostname != "" + +# set the hostname in the network configuration file +grep -v HOSTNAME /etc/sysconfig/network > /etc/sysconfig/network.cobbler +echo "HOSTNAME=$hostname" >> /etc/sysconfig/network.cobbler +rm -f /etc/sysconfig/network +mv /etc/sysconfig/network.cobbler /etc/sysconfig/network + +# Also set the hostname now, some applications require it +# (e.g.: if we're connecting to Puppet before a reboot). +/bin/hostname $hostname + #end if + #if $enableipv6 == True +grep -v NETWORKING_IPV6 /etc/sysconfig/network > /etc/sysconfig/network.cobbler +echo "NETWORKING_IPV6=yes" >> /etc/sysconfig/network.cobbler +rm -f /etc/sysconfig/network +mv /etc/sysconfig/network.cobbler /etc/sysconfig/network + #if $ipv6_autoconfiguration != "" +grep -v IPV6_AUTOCONF /etc/sysconfig/network > /etc/sysconfig/network.cobbler + #if $ipv6_autoconfiguration == True +echo "IPV6_AUTOCONF=yes" >> /etc/sysconfig/network.cobbler + #else +echo "IPV6_AUTOCONF=no" >> /etc/sysconfig/network.cobbler + #end if +rm -f /etc/sysconfig/network +mv /etc/sysconfig/network.cobbler /etc/sysconfig/network + #end if + #if $ipv6_default_device != "" +grep -v IPV6_DEFAULTDEV /etc/sysconfig/network > /etc/sysconfig/network.cobbler +echo "IPV6_DEFAULTDEV=$ipv6_default_device" >> /etc/sysconfig/network.cobbler +rm -f /etc/sysconfig/network +mv /etc/sysconfig/network.cobbler /etc/sysconfig/network + #end if + #end if + ## ============================================================================= + ## now create the config file for each interface + #for $iname in $ikeys + +# Start configuration for $iname + ## create lots of variables to use later + #set $idata = $interfaces[$iname] + #set $mac = $idata.get("mac_address", "").upper() + #set $mtu = $idata.get("mtu", "") + #set $static = $idata.get("static", "") + #set $ip = $idata.get("ip_address", "") + #set $netmask = $idata.get("netmask", "") + #set $if_gateway = $idata.get("if_gateway", "") + #set $static_routes = $idata.get("static_routes", "") + #set $iface_type = $idata.get("interface_type", "").lower() + #set $iface_master = $idata.get("interface_master", "") + #set $bonding_opts = $idata.get("bonding_opts", "") + #set $bridge_opts = $idata.get("bridge_opts", "").split(" ") + #set $ipv6_address = $idata.get("ipv6_address", "") + #set $ipv6_secondaries = $idata.get("ipv6_secondaries", "") + #set $ipv6_mtu = $idata.get("ipv6_mtu", "") + #set $ipv6_default_gateway = $idata.get("ipv6_default_gateway", "") + #set $ipv6_static_routes = $idata.get("ipv6_static_routes", "") + #set $devfile = "/etc/sysconfig/network-scripts/cobbler/ifcfg-" + $iname + #set $routesfile = "/etc/sysconfig/network-scripts/cobbler/route-" + $iname + #set $ipv6_routesfile = "/etc/sysconfig/network-scripts/cobbler/route6-" + $iname + ## determine if this interface is for a VLAN + #if $vlanpattern.match($iname) + #set $is_vlan = "true" + #else + #set $is_vlan = "false" + #end if + ## slave interfaces are assumed to be static + #if $iface_type in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + #set $static = 1 + #end if + ## =================================================================== + ## Things every interface get, no matter what + ## =================================================================== +echo "DEVICE=$iname" > $devfile +echo "ONBOOT=yes" >> $devfile + #if $mac != "" and $iface_type not in ("master","bond","bridge","bonded_bridge_slave") + ## virtual interfaces don't get MACs +echo "HWADDR=$mac" >> $devfile +IFNAME=\$(ip -o link | grep -i '$mac' | sed -e 's/^[0-9]*: //' -e 's/:.*//') + ## Rename this interface in modprobe.conf + ## FIXME: if both interfaces startwith eth this is wrong +if [ -f "/etc/modprobe.conf" ] && [ \$IFNAME ]; then + grep \$IFNAME /etc/modprobe.conf | sed "s/\$IFNAME/$iname/" >> /etc/modprobe.conf.cobbler + grep -v \$IFNAME /etc/modprobe.conf >> /etc/modprobe.conf.new + rm -f /etc/modprobe.conf + mv /etc/modprobe.conf.new /etc/modprobe.conf +fi + #end if + ## =================================================================== + ## Actions based on interface_type + ## =================================================================== + #if $iface_type in ("master","bond","bonded_bridge_slave") + ## if this is a bonded interface, configure it in modprobe.conf + #if $osversion == "rhel4" +if [ -f "/etc/modprobe.conf" ]; then + echo "install $iname /sbin/modprobe bonding -o $iname $bonding_opts" >> /etc/modprobe.conf.cobbler +fi + #else + ## Add required entry to modprobe.conf +if [ -f "/etc/modprobe.conf" ]; then + echo "alias $iname bonding" >> /etc/modprobe.conf.cobbler +fi + #end if + #if $bonding_opts != "" +cat >> $devfile << EOF +BONDING_OPTS="$bonding_opts" +EOF + #end if + #elif $iface_type in ("slave","bond_slave") and $iface_master != "" +echo "SLAVE=yes" >> $devfile +echo "MASTER=$iface_master" >> $devfile +echo "HOTPLUG=no" >> $devfile + #end if + #if $iface_type == "bridge" +echo "TYPE=Bridge" >> $devfile + #for $bridge_opt in $bridge_opts + #if $bridge_opt.strip() != "" +echo "$bridge_opt" >> $devfile + #end if + #end for + #elif ($iface_type == "bridge_slave" or $iface_type == "bonded_bridge_slave") and $iface_master != "" +echo "BRIDGE=$iface_master" >> $devfile +echo "HOTPLUG=no" >> $devfile + #end if + #if $iface_type != "bridge" +echo "TYPE=Ethernet" >> $devfile + #end if + ## =================================================================== + ## Actions based on static/dynamic configuration + ## =================================================================== + #if $static + #if $mac == "" and $iface_type == "" +# WARNING! Configuring interfaces by their names only +# is error-prone, and can cause issues if and when +# the kernel gives an interface a different name +# following a reboot/hardware changes. + #end if +echo "BOOTPROTO=none" >> $devfile + #if $ip != "" and $iface_type not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + ## Only configure static networking if an IP-address is configured + ## and if the interface isn't slaved to another interface (bridging or bonding) + #if $iname in $promisc_interfaces +echo "PROMISC=yes" >> $devfile + #else +echo "IPADDR=$ip" >> $devfile + #end if + #if $if_gateway != "" +echo "GATEWAY=$if_gateway" >> $devfile + #end if + #if $netmask == "" + ## Default to 255.255.255.0? + #set $netmask = "255.255.255.0" + #end if +echo "NETMASK=$netmask" >> $devfile + #end if + #if $enableipv6 == True and $ipv6_autoconfiguration == False + #if $ipv6_address != "" +echo "IPV6INIT=yes" >> $devfile +echo "IPV6ADDR=$ipv6_address" >> $devfile + #end if + #if $ipv6_secondaries != "" + #set ipv6_secondaries = ' '.join(ipv6_secondaries) + ## The quotes around the ipv6 ip's need to be here +echo "IPV6ADDR_SECONDARIES=\"$ipv6_secondaries\"" >> $devfile + #end if + #if $ipv6_mtu != "" +echo "IPV6MTU=$ipv6_mtu" >> $devfile + #end if + #if $ipv6_default_gateway != "" +echo "IPV6_DEFAULTGW=$ipv6_default_gateway" >> $devfile + #end if + #end if + #else + ## this is a DHCP interface, much less work to do +echo "BOOTPROTO=dhcp" >> $devfile + #if $len($name_servers) > 0 +echo "PEERDNS=no" >> $devfile + #end if + #end if + ## =================================================================== + ## VLAN configuration + ## =================================================================== + #if $is_vlan == "true" +echo "VLAN=yes" >> $devfile +echo "ONPARENT=yes" >> $devfile + #end if + ## =================================================================== + ## Optional configuration stuff + ## =================================================================== + #if $mtu != "" +echo "MTU=$mtu" >> $devfile + #end if + ## =================================================================== + ## Non-slave DNS configuration, when applicable + ## =================================================================== + ## If the interface is anything but a slave then add DNSn entry + #if $iface_type.lower() not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + #set $nct = 0 + #for $nameserver in $name_servers + #set $nct = $nct + 1 +echo "DNS$nct=$nameserver" >> $devfile + #end for + #end if + ## =================================================================== + ## Interface route configuration + ## =================================================================== + #for $route in $static_routes + #set routepattern = $re.compile("[0-9/.]+:[0-9.]+") + #if $routepattern.match($route) + #set $routebits = $route.split(":") + #set [$network, $router] = $route.split(":") +echo "$network via $router" >> $routesfile + #else +# Warning: invalid route "$route" + #end if + #end for + #if $enableipv6 == True + #for $route in $ipv6_static_routes + #set routepattern = $re.compile("[0-9a-fA-F:/]+,[0-9a-fA-F:]+") + #if $routepattern.match($route) + #set $routebits = $route.split(",") + #set [$network, $router] = $route.split(",") +echo "$network via $router dev $iname" >> $ipv6_routesfile + #else +# Warning: invalid ipv6 route "$route" + #end if + #end for + #end if + ## =================================================================== + ## Done with this interface + ## =================================================================== + #set $i = $i + 1 +# End configuration for $iname + #end for + ## ============================================================================= + ## Configure name server search path in /etc/resolv.conf + #set $num_ns = $len($name_servers) + #set $num_ns_search = $len($name_servers_search) + #if $num_ns_search > 0 + +sed -i -e "/^search /d" /etc/resolv.conf +echo -n "search " >>/etc/resolv.conf + #for $nameserversearch in $name_servers_search +echo -n "$nameserversearch " >>/etc/resolv.conf + #end for +echo "" >>/etc/resolv.conf + #end if + ## ============================================================================= + ## Configure name servers in /etc/resolv.conf + #if $num_ns > 0 + +sed -i -e "/^nameserver /d" /etc/resolv.conf + #for $nameserver in $name_servers +echo "nameserver $nameserver" >>/etc/resolv.conf + #end for + #end if + +## Disable all eth interfaces by default before overwriting +## the old files with the new ones in the working directory +## This stops unneccesary (and time consuming) DHCP queries +## during the network initialization +sed -i 's/ONBOOT=yes/ONBOOT=no/g' /etc/sysconfig/network-scripts/ifcfg-eth* + +## Move all staged files to their final location +rm -f /etc/sysconfig/network-scripts/ifcfg-* +mv /etc/sysconfig/network-scripts/cobbler/* /etc/sysconfig/network-scripts/ +rm -r /etc/sysconfig/network-scripts/cobbler +if [ -f "/etc/modprobe.conf" ]; then +cat /etc/modprobe.conf.cobbler >> /etc/modprobe.conf +rm -f /etc/modprobe.conf.cobbler +fi +#end if +# End post_install_network_config generated code diff --git a/cobbler/snippets/post_install_network_config_deb b/cobbler/snippets/post_install_network_config_deb new file mode 100644 index 0000000..01548b6 --- /dev/null +++ b/cobbler/snippets/post_install_network_config_deb @@ -0,0 +1,231 @@ +# Start post_install_network_config generated code +#if $getVar("system_name","") != "" + ## this is being provisioned by system records, not profile records + ## so we can do the more complex stuff + ## get the list of interface names + #set ikeys = $interfaces.keys() + #set osversion = $getVar("os_version","") + #import re + #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") + ## Determine if we should use the MAC address to configure the interfaces first + ## Only physical interfaces are required to have a MAC address + ## Also determine the number of bonding devices we have, so we can set the + ## max-bonds option in modprobe.conf accordingly. -- jcapel + #set $configbymac = True + #set $bridge_slaves = {} + #set $numbondingdevs = 0 + #set $enableipv6 = False + ## ============================================================================= + #for $iname in $ikeys + ## look at the interface hash data for the specific interface + #set $idata = $interfaces[$iname] + ## do not configure by mac address if we don't have one AND it's not for bonding/vlans + ## as opposed to a "real" physical interface + #if $idata.get("mac_address", "") == "" and not $vlanpattern.match($iname) and not $idata.get("interface_type", "").lower() in ("master","bond","bridge"): + ## we have to globally turn off the config by mac feature as we can't + ## use it now + #set $configbymac = False + #end if + ## count the number of bonding devices we have. + #if $idata.get("interface_type", "").lower() in ("master","bond","bonded_bridge_slave") + #set $numbondingdevs += 1 + #end if + ## build a mapping of bridge slaves, since deb/ubuntu bridge slaves do not + ## get interface entries of their own + #if $idata.get("interface_type","").lower() == "bridge_slave" + #set $this_master = $idata.get("interface_master", None) + #if $this_master and not $bridge_slaves.has_key($this_master) + #set $bridge_slaves[$this_master] = [] + #end if + <% bridge_slaves[this_master].append(iname) %> + #end if + ## enable IPv6 networking if we set an ipv6 address or turn on autoconfiguration + #if $idata.get("ipv6_address", "") != "" or $ipv6_autoconfiguration == True + #set $enableipv6 = True + #end if + #end for + ## end looping through the interfaces to see which ones we need to configure. + ## ============================================================================= + ## Rewrite the interfaces file and make sure we preserve the loopback device +rm -f /etc/network/interfaces +touch /etc/network/interfaces +echo "auto lo" >> /etc/network/interfaces +echo "iface lo inet loopback" >> /etc/network/interfaces +echo "" >> /etc/network/interfaces + ## ============================================================================= + ## now create the config file for each interface + #for $iname in $ikeys + ## create lots of variables to use later + #set $idata = $interfaces[$iname] + #set $mac = $idata.get("mac_address", "").upper() + #set $mtu = $idata.get("mtu", "") + #set $static = $idata.get("static", "") + #set $ip = $idata.get("ip_address", "") + #set $netmask = $idata.get("netmask", "") + #set $if_gateway = $idata.get("if_gateway", "") + #set $static_routes = $idata.get("static_routes", "") + #set $iface_type = $idata.get("interface_type", "").lower() + #set $iface_master = $idata.get("interface_master", "") + #set $bonding_opts = $idata.get("bonding_opts", "") + #set $bridge_opts = $idata.get("bridge_opts", "").split(" ") + #set $ipv6_address = $idata.get("ipv6_address", "") + #set $ipv6_secondaries = $idata.get("ipv6_secondaries", "") + #set $ipv6_mtu = $idata.get("ipv6_mtu", "") + #set $ipv6_default_gateway = $idata.get("ipv6_default_gateway", "") + #set $ipv6_static_routes = $idata.get("ipv6_static_routes", "") + #set $devfile = "/etc/sysconfig/network-scripts/cobbler/ifcfg-" + $iname + #set $routesfile = "/etc/sysconfig/network-scripts/cobbler/route-" + $iname + #set $ipv6_routesfile = "/etc/sysconfig/network-scripts/cobbler/route6-" + $iname + ## determine if this interface is for a VLAN + #if $vlanpattern.match($iname) + #set $is_vlan = "true" + #else + #set $is_vlan = "false" + #end if + ## slave interfaces are assumed to be static + #if $iface_type in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") + #set $static = 1 + #end if + ## =================================================================== + ## Things every interface get, no matter what + ## =================================================================== +echo "auto $iname" >> /etc/network/interfaces + ## =================================================================== + ## Actions based on interface_type + ## =================================================================== + #if $iface_type in ("master","bond","bonded_bridge_slave") + #pass + #elif $iface_type in ("slave","bond_slave") and $iface_master != "" + #pass + #elif $iface_type == "bridge" + #set $slave_ports = " ".join($bridge_slaves.get($iname,[])) + #if $slave_ports != "" +echo " bridge_ports $slave_ports" >> /etc/network/interfaces + #end if + #for $bridge_opt in $bridge_opts + #if $bridge_opt.strip() != "" +echo " $bridge_opt" >> /etc/network/interfaces + #end if + #end for + #end if + ## =================================================================== + ## Actions based on static configuration + ## =================================================================== + #if $static + #if $ip != "" and $iface_type not in ("slave","bond_slave","bridge_slave","bonded_bridge_slave") +echo "iface $iname inet static" >> /etc/network/interfaces +echo " hwaddress $mac" >> /etc/network/interfaces +echo " address $ip" >> /etc/network/interfaces + #if $netmask != "" +echo " netmask $netmask" >> /etc/network/interfaces + #end if + #if $iface_type in ("master","bond") + #set $bondslaves = "" + #for $bondiname in $ikeys + #set $bondidata = $interfaces[$bondiname] + #set $bondiface_type = $bondidata.get("interface_type", "").lower() + #set $bondiface_master = $bondidata.get("interface_master", "") + #if $bondiface_master == $iname + #set $bondslaves += $bondiname + " " + #end if + #end for +echo " bond-slaves $bondslaves" >> /etc/network/interfaces + #for $bondopts in $bonding_opts.split(" ") + #set [$bondkey, $bondvalue] = $bondopts.split("=") +echo " bond-$bondkey $bondvalue" >> /etc/network/interfaces + #end for + #end if + #else +echo "iface $iname inet manual" >> /etc/network/interfaces + #end if + #if $iface_type in ("slave","bond_slave") and $iface_master != "" +echo "bond-master $iface_master" >> /etc/network/interfaces + #end if + #if $enableipv6 == True and $ipv6_autoconfiguration == False + #if $ipv6_address != "" + #pass + #end if + #if $ipv6_secondaries != "" + #set ipv6_secondaries = ' '.join(ipv6_secondaries) + #end if + #if $ipv6_mtu != "" + #pass + #end if + #if $ipv6_default_gateway != "" + #pass + #end if + #end if + #else +echo "iface $iname inet dhcp" >> /etc/network/interfaces + #end if + ## =================================================================== + ## VLAN configuration + ## =================================================================== + #if $is_vlan == "true" + #pass + #end if + ## =================================================================== + ## Optional configuration stuff + ## =================================================================== + #if $if_gateway != "" +echo " gateway $if_gateway" >> /etc/network/interfaces + #end if + #if $mtu != "" +echo " mtu $mtu" >> /etc/network/interfaces + #end if + ## =================================================================== + ## Interface route configuration + ## =================================================================== + #for $route in $static_routes + #set routepattern = $re.compile("[0-9/.]+:[0-9.]+") + #if $routepattern.match($route) + #set [$network, $router] = $route.split(":") +echo " up ip route add $network via $router dev $iname || true" >> /etc/network/interfaces + #else +echo " # Warning: invalid route: $route" >> /etc/network/interfaces + #end if + #end for + #if $enableipv6 == True + #for $route in $ipv6_static_routes + #set routepattern = $re.compile("[0-9a-fA-F:/]+,[0-9a-fA-F:]+") + #if $routepattern.match($route) + #set [$network, $router] = $route.split(",") +echo " up ip -6 route add $network via $router dev $iname || true" >> /etc/network/interfaces + #else +echo " # Warning: invalid route: $route" >> /etc/network/interfaces + #end if + #end for + #end if + ## =================================================================== + ## Done with this interface + ## =================================================================== + #end for + ## ============================================================================= + ## Configure the system's primary hostname. This is also passed to anaconda, but + ## anaconda doesn't seem to honour it in DHCP-setups. + #if $hostname != "" +echo "$hostname" > /etc/hostname +/bin/hostname $hostname + #end if + ## ============================================================================= + ## Configure name server search path in /etc/resolv.conf + #set $num_ns = $len($name_servers) + #set $num_ns_search = $len($name_servers_search) + #if $num_ns_search > 0 +sed -i -e "/^search /d" /etc/resolv.conf +echo -n "search " >>/etc/resolv.conf + #for $nameserversearch in $name_servers_search +echo -n "$nameserversearch " >>/etc/resolv.conf + #end for +echo "" >>/etc/resolv.conf + #end if + ## ============================================================================= + ## Configure name servers in /etc/resolv.conf + #if $num_ns > 0 +sed -i -e "/^nameserver /d" /etc/resolv.conf + #for $nameserver in $name_servers +echo "nameserver $nameserver" >>/etc/resolv.conf + #end for + #end if +#end if +# End post_install_network_config generated code diff --git a/cobbler/snippets/post_koan_add_reinstall_entry b/cobbler/snippets/post_koan_add_reinstall_entry new file mode 100644 index 0000000..63a9c59 --- /dev/null +++ b/cobbler/snippets/post_koan_add_reinstall_entry @@ -0,0 +1,6 @@ +%post +#if $getVar("system_name","") != "" + koan --server=$server --replace-self --add-reinstall-entry +#else + koan --server=$server --replace-self --profile=$profile_name --add-reinstall-entry +#end if diff --git a/cobbler/snippets/post_run_deb b/cobbler/snippets/post_run_deb new file mode 100644 index 0000000..7a67b3b --- /dev/null +++ b/cobbler/snippets/post_run_deb @@ -0,0 +1 @@ +# A general purpose snippet to add late-command actions for preseeds diff --git a/cobbler/snippets/post_s390_reboot b/cobbler/snippets/post_s390_reboot new file mode 100644 index 0000000..fa33f06 --- /dev/null +++ b/cobbler/snippets/post_s390_reboot @@ -0,0 +1,67 @@ +## RHEL zVM installs do not properly reboot into the installed system. This +## issue has been resolved in RHEL-5 Update3. To get a consistent reboot +## behavior for s390* installs on all distros, this snippet can be used. The +## snippet will attempt to discover the IPL volume zipl is being installed +## to and will attempt a reipl. Be sure to set this snippet as the *last* +## snippet your kickstart template. + +#if $arch.startswith("s390"): +%post --nochroot + +# Does the kickstart file request a reboot? +grep -q "^reboot" /tmp/ks.cfg /ks.cfg 2>/dev/null +if [ \$? -ne 0 ]; then + exit 0 +fi + +# find out the location of /boot and use it to re-ipl +boot_dev="" +for mountpt in /mnt/sysimage/boot /mnt/sysimage; +do + set -- \$(grep " \$mountpt " /proc/mounts) + if [ -b "\$1" ]; then + boot_dev=\$1 + break + fi +done + +# lookup dasd disk +if [[ \$boot_dev == *dasd* ]]; then + # remove the '/dev/' (aka basename) + boot_dev=\${boot_dev\#\#/[^/]*/} + # strip partition number from dasd device + boot_dev=\${boot_dev%%[0-9]} + type="ccw" + id=`basename \$(readlink /sys/block/\$boot_dev/device)` + + # HACK - In RHEL4 and RHEL3 ... we do it the hard way + grep -q "^[34]\$" /.buildstamp 2>/dev/null + if [ \$? -eq 0 ]; then + cat < /mnt/sysimage/tmp/zeboot.sh +\#!/bin/bash +/sbin/modprobe -r vmcp +rm -f "/dev/vmcp" +sleep 2 +[ -b "/dev/vmcp" ] || /bin/mknod /dev/vmcp c 10 61 +/sbin/modprobe -a vmcp +sync +# Force a boot (e.g. IPL 0100) +/sbin/vmcp ipl \${id\#\#*.} +EOF + /bin/chmod +x /mnt/sysimage/tmp/zeboot.sh + /bin/chroot /mnt/sysimage /tmp/zeboot.sh + # In RHEL5 ... lets cleanly shutdown (Update 3 and newer) + else + echo \$type > /sys/firmware/reipl/reipl_type + echo \$id > /sys/firmware/reipl/\$type/device + + # Force a reboot + pid=\$(cat /var/run/init.pid) + [ -z "\$pid" ] && pid=\$(pidof init) + kill -12 \$pid + pid=\$(cat /var/run/loader.run) + [ -z "\$pid" ] && pid=\$(pidof loader) + kill \$pid + fi +fi +#end if diff --git a/cobbler/snippets/pre_anamon b/cobbler/snippets/pre_anamon new file mode 100644 index 0000000..ea20460 --- /dev/null +++ b/cobbler/snippets/pre_anamon @@ -0,0 +1,4 @@ +#if $str($getVar('anamon_enabled','')) == "1" +wget -O /tmp/anamon "http://$server:$http_port/cobbler/aux/anamon" +python /tmp/anamon --name "$name" --server "$server" --port "$http_port" +#end if diff --git a/cobbler/snippets/pre_install_network_config b/cobbler/snippets/pre_install_network_config new file mode 100644 index 0000000..29f9ffa --- /dev/null +++ b/cobbler/snippets/pre_install_network_config @@ -0,0 +1,90 @@ +#if $getVar("system_name","") != "" +# Start pre_install_network_config generated code +#raw +# generic functions to be used later for discovering NICs +mac_exists() { + if which ip 2>/dev/null >/dev/null; then + ip -o link | grep -i "$1" 2>/dev/null >/dev/null + return $? + elif which esxcfg-nics 2>/dev/null >/dev/null; then + esxcfg-nics -l | grep -i "$1" 2>/dev/null >/dev/null + return $? + else + ifconfig -a | grep -i "$1" 2>/dev/null >/dev/null + return $? + fi +} +get_ifname() { + if which ip 2>/dev/null >/dev/null; then + IFNAME=$(ip -o link | grep -i "$1" | sed -e 's/^[0-9]*: //' -e 's/:.*//') + elif which esxcfg-nics 2>/dev/null >/dev/null; then + IFNAME=$(esxcfg-nics -l | grep -i "$1" | cut -d " " -f 1) + else + IFNAME=$(ifconfig -a | grep -i "$1" | cut -d " " -f 1) + if [ -z $IFNAME ]; then + IFNAME=$(ifconfig -a | grep -i -B 2 "$1" | sed -n '/flags/s/:.*$//p') + fi + fi +} +#end raw + #set ikeys = $interfaces.keys() + #import re + #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.:][0-9]+") + #set $routepattern = $re.compile("[0-9/.]+:[0-9.]+") + ## + #for $iname in $ikeys +# Start $iname + #set $idata = $interfaces[$iname] + #set $mac = $idata["mac_address"] + #set $static = $idata["static"] + # #set $management = $idata["management"] + #set $ip = $idata["ip_address"] + #set $netmask = $idata["netmask"] + #set $iface_type = $idata["interface_type"] + #set $iface_master = $idata["interface_master"] + #set $static_routes = $idata["static_routes"] + #set $devfile = "/etc/sysconfig/network-scripts/ifcfg-" + $iname + #if not $management or $mac == "" + #continue + #end if + #if $static and $ip != "" + #if $netmask == "" + ## Netmask not provided, default to /24. + #set $netmask = "255.255.255.0" + #end if + #set $netinfo = "--bootproto=static --ip=%s --netmask=%s" % ($ip, $netmask) + #if $gateway != "" + #set $netinfo = "%s --gateway=%s" % ($netinfo, $gateway) + #end if + #if $len($name_servers) > 0 + #set $netinfo = "%s --nameserver=%s" % ($netinfo, $name_servers[0]) + #end if + #else if not $static + #set $netinfo = "--bootproto=dhcp" + #else + ## Skip this interface, it's set as static, but without + ## networking info. +# Skipping (no configuration)... + #continue + #end if + #if $hostname != "" + #set $netinfo = "%s --hostname=%s" % ($netinfo, $hostname) + #end if +# Configuring $iname ($mac) +if mac_exists $mac +then + get_ifname $mac + echo "network --device=\$IFNAME $netinfo" >> /tmp/pre_install_network_config + #for $route in $static_routes + #if $routepattern.match($route) + #set $routebits = $route.split(":") + #set [$network, $router] = $route.split(":") + ip route add $network via $router dev \$IFNAME + #else + # Warning: invalid route "$route" + #end if + #end for +fi + #end for +# End pre_install_network_config generated code +#end if diff --git a/cobbler/snippets/pre_partition_select b/cobbler/snippets/pre_partition_select new file mode 100644 index 0000000..1d1e8f4 --- /dev/null +++ b/cobbler/snippets/pre_partition_select @@ -0,0 +1,33 @@ +# partition details calculation + +# Determine how many drives we have +set \$(list-harddrives) +let numd=\$#/2 +d1=\$1 +d2=\$3 + +# Determine architecture-specific partitioning needs +EFI_PART="" +PPC_PREP_PART="" +BOOT_PART="" + +case \$(uname -m) in + ia64) + EFI_PART="part /boot/efi --fstype vfat --size 200 --recommended" + ;; + ppc*) + PPC_PREP_PART="part None --fstype 'PPC PReP Boot' --size 8" + BOOT_PART="part /boot --fstype ext3 --size 200 --recommended" + ;; + *) + BOOT_PART="part /boot --fstype ext3 --size 200 --recommended" + ;; +esac + +cat << EOF > /tmp/partinfo +\$EFI_PART +\$PPC_PREP_PART +\$BOOT_PART +part / --fstype ext3 --size=1024 --grow --ondisk=\$d1 --asprimary +part swap --recommended --ondisk=\$d1 --asprimary +EOF diff --git a/cobbler/snippets/preseed_apt_repo_config b/cobbler/snippets/preseed_apt_repo_config new file mode 100644 index 0000000..2d3487c --- /dev/null +++ b/cobbler/snippets/preseed_apt_repo_config @@ -0,0 +1,22 @@ +# Additional repositories, local[0-9] available +#set $cur=0 +#set $repo_data = $getVar("repo_data",[]) +#for $repo in $repo_data + #for $dist in $repo.apt_dists + #set $comps = " ".join($repo.apt_components) +d-i apt-setup/local${cur}/repository string \ + #if $repo.mirror_locally + http://$http_server/cblr/repo_mirror/${repo.name} $dist $comps + #else + ${repo.mirror} $dist $comps + #end if + #if $repo.comment != "" +d-i apt-setup/local${cur}/comment string ${repo.comment} + #end if + #if $repo.breed == "src" +# Enable deb-src lines +d-i apt-setup/local${cur}/source boolean false + #end if + #set $cur=$cur+1 + #end for +#end for diff --git a/cobbler/snippets/proxy.xml b/cobbler/snippets/proxy.xml new file mode 100644 index 0000000..766cbb4 --- /dev/null +++ b/cobbler/snippets/proxy.xml @@ -0,0 +1,9 @@ + + true + + $proxy + + localhost, 127.0.0.1 + + + diff --git a/cobbler/snippets/puppet_install_if_enabled b/cobbler/snippets/puppet_install_if_enabled new file mode 100644 index 0000000..f554b8d --- /dev/null +++ b/cobbler/snippets/puppet_install_if_enabled @@ -0,0 +1,4 @@ +#if $str($getVar('puppet_auto_setup','')) == "1" +puppet +#end if + diff --git a/cobbler/snippets/puppet_register_if_enabled b/cobbler/snippets/puppet_register_if_enabled new file mode 100644 index 0000000..90ef702 --- /dev/null +++ b/cobbler/snippets/puppet_register_if_enabled @@ -0,0 +1,15 @@ +# start puppet registration +#if $str($getVar('puppet_auto_setup','')) == "1" +# generate puppet certificates and trigger a signing request, but +# don't wait for signing to complete +#if $int($getVar('puppet_version',2)) >= 3 +/usr/bin/puppet agent --test --waitforcert 0 #echo (($str($getVar('puppet_server','')) != '') and "--server '"+$str($getVar('puppet_server',''))+"'" or '') +#else +/usr/sbin/puppetd --test --waitforcert 0 #echo (($str($getVar('puppet_server','')) != '') and "--server '"+$str($getVar('puppet_server',''))+"'" or '') +#end if + +# turn puppet service on for reboot +/sbin/chkconfig puppet on + +#end if +# end puppet registration diff --git a/cobbler/snippets/redhat_register b/cobbler/snippets/redhat_register new file mode 100644 index 0000000..2f1f783 --- /dev/null +++ b/cobbler/snippets/redhat_register @@ -0,0 +1,18 @@ +# begin Red Hat management server registration +#if $redhat_management_type != "off" and $redhat_management_key != "" +mkdir -p /usr/share/rhn/ + #if $redhat_management_type == "site" + #set $mycert_file = "RHN-ORG-TRUSTED-SSL-CERT" + #set $mycert = "/usr/share/rhn/" + $mycert_file +wget http://$redhat_management_server/pub/RHN-ORG-TRUSTED-SSL-CERT -O $mycert +perl -npe 's/RHNS-CA-CERT/$mycert_file/g' -i /etc/sysconfig/rhn/* + #end if + #if $redhat_management_type == "hosted" + #set $mycert = "/usr/share/rhn/RHNS-CA-CERT" + #end if + #set $endpoint = "https://%s/XMLRPC" % $redhat_management_server +rhnreg_ks --serverUrl=$endpoint --sslCACert=$mycert --activationkey=$redhat_management_key +#else +# not configured to register to any Red Hat management server (ok) +#end if +# end Red Hat management server registration diff --git a/cobbler/snippets/restore_boot_device b/cobbler/snippets/restore_boot_device new file mode 100644 index 0000000..1b54e1f --- /dev/null +++ b/cobbler/snippets/restore_boot_device @@ -0,0 +1,6 @@ +if [ "$os_version" == "sles11" ]; then + nvsetenv boot-device "$(cat /root/inst-sys/boot-device.bak)" +elif [ "$os_version" == "fedora17" ]; then + # must be run from a %post --nochroot section + nvsetenv boot-device "$(cat /tmp/boot-device.bak)" +fi diff --git a/cobbler/snippets/rhn_certificate_based_register b/cobbler/snippets/rhn_certificate_based_register new file mode 100644 index 0000000..29ad193 --- /dev/null +++ b/cobbler/snippets/rhn_certificate_based_register @@ -0,0 +1,12 @@ +# begin Red Hat Network certificate-based server registration +#if $redhat_management_type == "cert" and $redhat_register_user != "" and $redhat_register_password != "" +# Subscribe (register) the system +subscription-manager register --autosubscribe --username=$redhat_register_user --password=$redhat_register_password +# Add what used to be called channels +yum -y install yum-utils +yum-config-manager --enable rhel-6-server-optional-rpms +yum-config-manager --enable rhel-6-server-supplementary +#else +# not configured to use Certificate-based RHN (ok) +#end if +# end Red Hat Network certificate-based server registration diff --git a/cobbler/snippets/rsyslogchef b/cobbler/snippets/rsyslogchef new file mode 100644 index 0000000..81bafea --- /dev/null +++ b/cobbler/snippets/rsyslogchef @@ -0,0 +1,14 @@ +\\$ModLoad imfile +# +\\$InputFileName /var/log/chef-client.log +\\$InputFileReadMode 0 +\\$InputFileTag +\\$InputFileStateFile firstboot_log +\\$InputFileSeverity notice +\\$InputFileFacility local3 +\\$InputRunFileMonitor +# +\\$InputFilePollInterval 1 +# +# +local3.info @$server:514 diff --git a/cobbler/snippets/rsyslogconf b/cobbler/snippets/rsyslogconf new file mode 100644 index 0000000..6de661d --- /dev/null +++ b/cobbler/snippets/rsyslogconf @@ -0,0 +1,87 @@ +# rsyslog v5 configuration file + +# For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html +# If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html + +#### MODULES #### +\\$ModLoad imuxsock # provides support for local system logging (e.g. via logger command) +# $ModLoad imklog # provides kernel logging support (previously done by rklogd) +#$ModLoad immark # provides --MARK-- message capability +\\$ModLoad imfile + +\\$WorkDirectory /var/lib/rsyslog +\\$ActionQueueType LinkedList +\\$ActionQueueFileName srvrfwd +\\$ActionResumeRetryCount -1 +\\$ActionQueueSaveOnShutDown on +*.* @@$server:514 + +# Provides UDP syslog reception +\\$ModLoad imudp +\\$UDPServerRun 514 + +# Provides TCP syslog reception +\\$ModLoad imtcp +\\$InputTCPServerRun 514 + + +#### GLOBAL DIRECTIVES #### + +# Use default timestamp format +\\$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +# File syncing capability is disabled by default. This feature is usually not required, +# not useful and an extreme performance hit +#$ActionFileEnableSync on + +# Include all config files in /etc/rsyslog.d/ +\\$IncludeConfig /etc/rsyslog.d/*.conf + + +#### RULES #### + +# Log all kernel messages to the console. +# Logging much else clutters up the screen. +#kern.* /dev/console + +# Log anything (except mail) of level info or higher. +# Don't log private authentication messages! +*.info;mail.none;authpriv.none;cron.none /var/log/messages +# The authpriv file has restricted access. +#authpriv.* /etc/chef/test.log +#authpriv.* @@$server:514 +# Log all the mail messages in one place. +mail.* -/var/log/maillog + + +# Log cron stuff +cron.* /var/log/cron + +# Everybody gets emergency messages +*.emerg * + +# Save news errors of level crit and higher in a special file. +uucp,news.crit /var/log/spooler + +# Save boot messages also to boot.log +local7.* /var/log/boot.log + + +# ### begin forwarding rule ### +# The statement between the begin ... end define a SINGLE forwarding +# rule. They belong together, do NOT split them. If you create multiple +# forwarding rules, duplicate the whole block! +# Remote Logging (we use TCP for reliable delivery) +# +# An on-disk queue is created for this action. If the remote host is +# down, messages are spooled to disk and sent when it is up again. +#$WorkDirectory /var/lib/rsyslog # where to place spool files +#$ActionQueueFileName fwdRule1 # unique name prefix for spool files +#$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible) +#$ActionQueueSaveOnShutdown on # save messages to disk on shutdown +#$ActionQueueType LinkedList # run asynchronously +#$ActionResumeRetryCount -1 # infinite retries if host is down +# remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional + + +# ### end of the forwarding rule ### diff --git a/cobbler/snippets/save_boot_device b/cobbler/snippets/save_boot_device new file mode 100644 index 0000000..2afb63f --- /dev/null +++ b/cobbler/snippets/save_boot_device @@ -0,0 +1,5 @@ +if [ "$os_version" == "sles11" ]; then + nvram --print-config=boot-device > /root/boot-device.bak +elif [ "$os_version" == "fedora17" ]; then + nvram --print-config=boot-device > /tmp/boot-device.bak +fi diff --git a/cobbler/snippets/suse_scriptwrapper.xml b/cobbler/snippets/suse_scriptwrapper.xml new file mode 100644 index 0000000..7bf4cf5 --- /dev/null +++ b/cobbler/snippets/suse_scriptwrapper.xml @@ -0,0 +1,12 @@ + diff --git a/cobbler/snippets/yum.conf b/cobbler/snippets/yum.conf new file mode 100644 index 0000000..ed6fd34 --- /dev/null +++ b/cobbler/snippets/yum.conf @@ -0,0 +1,28 @@ +[main] +cachedir=/var/cache/yum/$basearch/$releasever +#if $getVar('proxy', '') != "" +proxy=$proxy +#end if +keepcache=0 +debuglevel=2 +logfile=/var/log/yum.log +exactarch=1 +obsoletes=1 +gpgcheck=1 +plugins=1 +installonly_limit=5 +bugtracker_url=http://bugs.centos.org/set_project.php?project_id=16&ref=http://bugs.centos.org/bug_report_page.php?category=yum +distroverpkg=centos-release + +# This is the default, if you make this bigger yum won't see if the metadata +# is newer on the remote and so you'll "gain" the bandwidth of not having to +# download the new metadata and "pay" for it by yum not having correct +# information. +# It is esp. important, to have correct metadata, for distributions like +# Fedora which don't keep old packages around. If you don't like this checking +# interupting your command line usage, it's much better to have something +# manually check the metadata once an hour (yum-updatesd will do this). +# metadata_expire=90m + +# PUT YOUR REPOS HERE OR IN separate files named file.repo +# in /etc/yum.repos.d