Merge lp:~marcoceppi/charms/precise/wordpress/trunk into lp:charms/wordpress

Proposed by Marco Ceppi
Status: Merged
Approved by: Mark Mims
Approved revision: no longer in the source branch.
Merged at revision: 55
Proposed branch: lp:~marcoceppi/charms/precise/wordpress/trunk
Merge into: lp:charms/wordpress
Diff against target: 2597 lines (+2340/-56) (has conflicts)
24 files modified
README.md (+152/-0)
config.yaml (+26/-0)
copyright (+1/-1)
files/_debug/apc.php (+1364/-0)
files/_debug/info.php (+3/-0)
files/charm/nginx/etc_nginx_nginx.conf (+58/-0)
files/charm/nginx/etc_nginx_sites-enabled_loadbalancer (+51/-0)
files/charm/nginx/etc_nginx_sites-enabled_wordpress (+72/-0)
files/charm/php/php5-fpm_pool.d_www.conf (+41/-0)
files/charm/php/php5_conf.d_apc.ini (+13/-0)
hooks/config-changed (+54/-0)
hooks/db-relation-changed (+94/-46)
hooks/db-relation-departed (+5/-0)
hooks/install (+56/-1)
hooks/loadbalancer-relation-joined (+124/-0)
hooks/nfs-relation-changed (+28/-0)
hooks/nfs-relation-departed (+20/-0)
hooks/restart (+4/-0)
hooks/start (+3/-0)
hooks/stop (+5/-2)
hooks/upgrade-charm (+11/-0)
inc/common (+139/-0)
metadata.yaml (+12/-6)
revision (+4/-0)
Text conflict in hooks/db-relation-changed
Text conflict in metadata.yaml
Text conflict in revision
To merge this branch: bzr merge lp:~marcoceppi/charms/precise/wordpress/trunk
Reviewer Review Type Date Requested Status
Mark Mims (community) Approve
charmers Pending
Benjamin Kerensa Pending
Review via email: mp+120559@code.launchpad.net

This proposal supersedes a proposal from 2012-08-07.

Description of the change

There are a great many changes made to this charm, I will try to sum them up:

DO ALL THE THINGS THE OMGUBUNTU SITE DOES
Remove all the OMG! Ubuntu specific items
Allow for the use of NFS mounts to share data for lazy bloggers who are popular
Provide three configuration options for users: bare, single, optimized

To post a comment you must log in.
Revision history for this message
Benjamin Kerensa (bkerensa) wrote : Posted in a previous version of this proposal

Looks sane to me.

review: Approve
Revision history for this message
Mark Mims (mark-mims) wrote : Posted in a previous version of this proposal

Ok, here's a static review: This is an _example_ charm we'll use in charmschools and demos, so I'll have more stuff to say than when reviewing a normal charm :)

# things to change

- love the config and especially the config-changed hook... it's very simple, organized, and demoable! Please put explicit values into config.yaml... please don't make me guess if I should put `Optimized` or `optimized`

- so `charm proof` gives `I: relation shared-fs has no hooks`. I understand the desire to add a more general relation than nfs, but please remove this metadata until it's implemented to keep it clean and simple for people to read (demo charm)

- instead of `files/wordpress/wp-content`, please pull themes and plugins from external repos as needed... it's just extra cruft in the charm and it seems like it could grow _considerably_ and change at a faster/different rate than the charm itself. We want this to be best-practice for charms (demo charm). Perhaps we should discuss this one in channel with the gang or on the list to get a consensus on this, but I'd certainly prefer configurable external repos.

- the `stop` hook references omgbackup... that's great but make it generic please

- I'd love to see some of your `files/charm` contents be actual templates where more stuff is externalized via config. e.g., ports and stuff. Well, ok... after looking some more, some of this stuff will *need* to be parametrized... i.e., there're urls littering the static config files. Those need to be in `config.yaml`

# discussion/recommendations

- it's unclear what `deets` is and why that's not coming from upstream repos

- `upgrade-charm` hook should maybe provide a way to change nginx config too?

Thanks man... totally excited about getting this pimped-out version in the main charm! we need more like this.

review: Needs Fixing
Revision history for this message
Marco Ceppi (marcoceppi) wrote : Posted in a previous version of this proposal

* Explicit values added (and config-changed updated to convert incoming values to lower case)
* Shared-fs relation removed, that will be added once Gluster charm is ready
* External repo added, currently defaults to github:jujutools/wordpress-site will be moved to an LP branch at a later date
* All references to OMGUbuntu removed
  * A backup charm would be a great idea, for now it's just not in the charm
* deets has been moved to _debug and is now a config option
* upgrade-charm now re-runs the hooks/config-changed to make sure everything gets re-set up

Revision history for this message
Mark Mims (mark-mims) wrote :

awesome man... thanks!

debug is brilliant... we should perhaps add that as a "best practice" for charming... enable debug configs for the service itself if it supports it.

review: Approve
Revision history for this message
Mark Mims (mark-mims) wrote :

marco, merge away.

Please watch out for the conflicts.

55. By Marco Ceppi

* Nginx as a webserver
* Allow for scale-out with nginx
* Track Themes, Plugins, and other custom files in remote repo
* Cache levels are user-definable
* Share files between nodes with NFS
* Lots of performance enhancements

# Todo
* Add memcache support
* Wider support for remote files

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'README.md'
--- README.md 1970-01-01 00:00:00 +0000
+++ README.md 2012-08-21 13:22:29 +0000
@@ -0,0 +1,152 @@
1# Overview
2
3WordPress is a powerful blogging platform written in PHP. This charm aims to deploy WordPress in a fashion that will allow anyone to scale and grow out
4a single installation.
5
6# Installation
7
8This charm is available in the Juju Charm Store, to deploy you'll need at a minimum: a cloud environment, a working Juju installation,
9and a successful bootstrap. Please refer to the [Juju Getting Started](https://juju.ubuntu.com/docs/getting-started.html) documentation before continuing.
10
11Once bootstrapped, deploy the MySQL charm then this WordPress charm:
12
13 juju deploy mysql
14 juju deploy wordpress
15
16Add a relation between the two of them
17
18 juju add-relation wordpress mysql
19
20Expose the WordPress installation
21
22 juju expose wordpress
23
24# Configuration
25
26This WordPress charm comes with several tuning levels designed to encompass the different styles in which this charm will be used.
27
28## Tuning
29
30A use case for each tuning style is outlined below:
31
32### Bare
33
34The Bare configuration option is meant for those who wish to run the stock WordPress setup with no caching, no manipulation of data,
35and no additional scale out features enabled. This is ideal if you intend to install additional plugins to deal with coordinating
36WordPress units or simply wish to test drive WordPress as it is out of the box. This will still create a load-balancer when an additional
37unit is created, though everything else will be turned off (WordPress caching, APC OpCode caching, and NFS file sharing).
38
39To run this WordPress charm under a bare tuning level execute the following:
40
41 juju set wordpress tuning=bare
42
43### Single
44
45When running in Single mode, this charm will make every attempt to provide a solid base for your WordPress install. By running in single
46the following will be enabled: Nginx microcache, APC OpCode caching, WordPress caching module, and the ability to sync files via NFS.
47While Single mode is designed to allow for scaling out, it's meant to only scale out for temporary relief; say in the event of a large
48traffic in-flux. It's recommended for long running scaled out versions that optimized is used. The removal of the file share speeds up
49the site and servers ensuring that the most efficient set up is provided.
50
51To run this WordPress charm under a single tuning level execute the following:
52
53 juju set wordpress tuning=single
54
55### Optimized
56
57If you need to run WordPress on more than one instance constantly, or require scaling out and in on a regular basis, then Optimized is the
58recommended configuration. When you run WordPress under an Optimized tuning level, the ability to install, edit, and upgrade themes and plugins
59is disabled. By doing this the charm can drop the need for an NFS mount which is inefficient and serve everything from it's local disk.
60Everything else provided in Single level is available. In order to install or modify plugins with this setup you'll need to edit and commit
61them to a forked version of the charm in the files/wordpress/ directory.
62
63To run this WordPress charm under an optimized tuning level execute the following:
64
65 juju set wordpress tuning=optimized
66
67## wp-content
68
69In order to allow for custom WordPress content within the Juju charm a separate configuration option exists for pointing to any Git or Bzr
70repository. An example of a valid formed wp-content repository can be found on the [Juju Tools Github page](https://github.com/jujutools/wordpress-site).
71To set the wp-content directive to a git repository, use one of the following formats making sure to replace items like `host`, `path`, and `repo` with their
72respective names:
73
74 juju set wordpress wp-content=git@host:path/repo.git
75
76or
77
78 juju set wordpress wp-content=http://host/path/repo.git
79
80or
81
82 juju set wordpress wp-content=git://host/path/repo.git
83
84If you wish to use a bzr repository, then apply one of the following schemes replacing items like `host`, `username`, `path`, and `repo` with their respective values:
85
86For LaunchPad hosted repostiories:
87
88 juju set wordpress wp-content=lp:~username/path/repo
89
90For other Bzr repositories:
91
92 juju set wordpress wp-content=bzr://host/path/repo
93
94or
95
96 juju set wordpress wp-content=bzr+ssh://host/path/repo
97
98Setting the wp-content option to an empty string ("") will result in no further updates being pulled from that repository; however, the last pull will remain
99on the system and will not be removed.
100
101## debug
102
103This option will create a directory `_debug` at the root of each unit (`http://unit-address/_debug`). In this directory are two scripts: info.php and apc.php. info.php
104is a simple phpinfo script that will outline exactly how the environment is configured. apc.php is the APC admin portal which provides APC caching details in addition
105to several administrative functions like clearing the APC cache. This should never be set to "yes" in production as it exposes detailed information about the environments
106and may provide a way for an intruder to DDoS the machine.
107
108 juju set wordpress debug=yes
109
110to disable
111
112 juju set wordpress debug=no
113
114The default is to have debugging disabled.
115
116# Caveats
117
118## Single mode and the scale-out
119
120If you're in Single mode and you want to/need to scale out, but you've been upgrading, modifying, and installing plugins + themes like
121a normal WordPress user on a normal install; you can still scale out but you'll need to deploy a shared-fs charm first. At the time of
122this writing only the NFS charm will work, but as more shared-fs charms come out (gluster, ceph, etc) that provide a shared-fs/mount
123interface those should all work as well. In this example we'll use NFS:
124
125 juju deploy nfs
126 juju add-relation nfs wordpress:nfs
127
128By doing so, everything in the wp-contents directory is moved to this NFS mount and then shared to all future WordPress units. It's strongly
129recommended that you first deploy the nfs mount, _then_ scale WordPress out. Failure to do so may result in data loss. Once nfs is deployed,
130running, and related you can scale out the WordPress unit using the following command:
131
132 juju add-unit wordpress
133
134In the event you want more than one unit at a time (and do not wish to run the add-unit command multiple times) you can supply a `-n` number
135of units to add, so to add three more units:
136
137 juju add-unit -n3 wordpress
138
139## I don't want to run three different machines for one WP install
140
141There is a "hack" that will allow you to deploy multiple full services to the same machine as the bootstrap node, this has nothing to do with
142the charm, but it's something that comes up more than once. Use this, of course, at your own risk. At any time the Juju developers may smart
143up and decide to remove this configuration option from the `environments.yaml` file. Prior to your first deployment you'll need to add the
144following line to your Juju Environments file:
145
146 placement: local
147
148This will say "Everything that you deploy, will go on the bootstrap node". Make sure you plan to have a big enough bootstrap node to house
149both your database and WordPress install. After you've bootstrap'd the environment, deploy the MySQL and WordPress charms like you normally
150would. Instead of seeing three nodes you'll only see one, but both of your services will have been deployed. *FROM THIS POINT* you should
151either remove or comment out the `placement` line in the environments file. This will prevent issues from occurring when you try to deploy
152additional services or try to scale out existing services.
0153
=== added file 'config.yaml'
--- config.yaml 1970-01-01 00:00:00 +0000
+++ config.yaml 2012-08-21 13:22:29 +0000
@@ -0,0 +1,26 @@
1options:
2 tuning:
3 type: string
4 default: "single"
5 description: |
6 This is the tuning level for the WordPress setup. There are three options: "bare", "single", and "optimized".
7 "bare" will give you a nearly un-altered WordPress setup, as if you'd downloaded and set it up yourself.
8 "single" will provide you with everything you need to run a singlular unit of WordPress. This doesn't take in to
9 consideration that you'll be scaling at all. However, it will allow you to use WordPress free of any troubles and pesky
10 limitations that typically happen during "optimized". While you _can_ scale out with this setting I encourage you read the README
11 "optimized" will give you a hardened WordPress setup. Some of the features in the Admin panel will be locked down and theme edits/plugins
12 can only be updated through he charm. This is the recommended setup for those who are in serious need of constant scaling.
13 wp-content:
14 type: string
15 default: "https://github.com/jujutools/wordpress-site.git"
16 description: |
17 This is a full repository path to where the WordPress wp-contents can be found. At this time both Git and Bzr are
18 supported. An example of what a wp-content repository should look like can be found at http://github.com/jujutools/wordpress-site.
19 debug:
20 type: string
21 default: "no"
22 description: |
23 Setting this option to "yes" will expose /_debug on all instances over HTTP. In the _debug folder are two scripts, info.php and apc.php.
24 info.php will display the phpinfo information for that server while the apc.php will provide APC cache stats (as well as additional administrative
25 options for APC).
26
027
=== modified file 'copyright'
--- copyright 2011-06-10 11:20:27 +0000
+++ copyright 2012-08-21 13:22:29 +0000
@@ -1,7 +1,7 @@
1Format: http://dep.debian.net/deps/dep5/1Format: http://dep.debian.net/deps/dep5/
22
3Files: *3Files: *
4Copyright: Copyright 2011, Canonical Ltd., All Rights Reserved.4Copyright: Copyright 2012, Marco Ceppi, All Rights Reserved.
5License: GPL-35License: GPL-3
6 This program is free software: you can redistribute it and/or modify6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by7 it under the terms of the GNU General Public License as published by
88
=== added directory 'files'
=== added directory 'files/_debug'
=== added file 'files/_debug/apc.php'
--- files/_debug/apc.php 1970-01-01 00:00:00 +0000
+++ files/_debug/apc.php 2012-08-21 13:22:29 +0000
@@ -0,0 +1,1364 @@
1<?php
2/*
3 +----------------------------------------------------------------------+
4 | APC |
5 +----------------------------------------------------------------------+
6 | Copyright (c) 2006-2011 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
16 | Authors: Ralf Becker <beckerr@php.net> |
17 | Rasmus Lerdorf <rasmus@php.net> |
18 | Ilia Alshanetsky <ilia@prohost.org> |
19 +----------------------------------------------------------------------+
20
21 All other licensing and usage conditions are those of the PHP Group.
22
23*/
24
25$VERSION='$Id$';
26
27////////// READ OPTIONAL CONFIGURATION FILE ////////////
28if (file_exists("apc.conf.php")) include("apc.conf.php");
29////////////////////////////////////////////////////////
30
31////////// BEGIN OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////
32
33defaults('USE_AUTHENTICATION',0); // Use (internal) authentication - best choice if
34 // no other authentication is available
35 // If set to 0:
36 // There will be no further authentication. You
37 // will have to handle this by yourself!
38 // If set to 1:
39 // You need to change ADMIN_PASSWORD to make
40 // this work!
41defaults('ADMIN_USERNAME',''); // Admin Username
42defaults('ADMIN_PASSWORD',''); // Admin Password - CHANGE THIS TO ENABLE!!!
43
44// (beckerr) I'm using a clear text password here, because I've no good idea how to let
45// users generate a md5 or crypt password in a easy way to fill it in above
46
47//defaults('DATE_FORMAT', "d.m.Y H:i:s"); // German
48defaults('DATE_FORMAT', ' m/d/Y h:i:s A'); // US
49
50defaults('GRAPH_SIZE',150); // Image size
51
52//defaults('PROXY', 'tcp://127.0.0.1:8080');
53
54////////// END OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////////
55
56
57// "define if not defined"
58function defaults($d,$v) {
59 if (!defined($d)) define($d,$v); // or just @define(...)
60}
61
62// rewrite $PHP_SELF to block XSS attacks
63//
64$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],''), ENT_QUOTES, 'UTF-8') : '';
65$time = time();
66$host = php_uname('n');
67if($host) { $host = '('.$host.')'; }
68if (isset($_SERVER['SERVER_ADDR'])) {
69 $host .= ' ('.$_SERVER['SERVER_ADDR'].')';
70}
71
72// operation constants
73define('OB_HOST_STATS',1);
74define('OB_SYS_CACHE',2);
75define('OB_USER_CACHE',3);
76define('OB_SYS_CACHE_DIR',4);
77define('OB_VERSION_CHECK',9);
78
79// check validity of input variables
80$vardom=array(
81 'OB' => '/^\d+$/', // operational mode switch
82 'CC' => '/^[01]$/', // clear cache requested
83 'DU' => '/^.*$/', // Delete User Key
84 'SH' => '/^[a-z0-9]+$/', // shared object description
85
86 'IMG' => '/^[123]$/', // image to generate
87 'LO' => '/^1$/', // login requested
88
89 'COUNT' => '/^\d+$/', // number of line displayed in list
90 'SCOPE' => '/^[AD]$/', // list view scope
91 'SORT1' => '/^[AHSMCDTZ]$/', // first sort key
92 'SORT2' => '/^[DA]$/', // second sort key
93 'AGGR' => '/^\d+$/', // aggregation by dir level
94 'SEARCH' => '~^[a-zA-Z0-1/_.-]*$~' // aggregation by dir level
95);
96
97// default cache mode
98$cache_mode='opcode';
99
100// cache scope
101$scope_list=array(
102 'A' => 'cache_list',
103 'D' => 'deleted_list'
104);
105
106// handle POST and GET requests
107if (empty($_REQUEST)) {
108 if (!empty($_GET) && !empty($_POST)) {
109 $_REQUEST = array_merge($_GET, $_POST);
110 } else if (!empty($_GET)) {
111 $_REQUEST = $_GET;
112 } else if (!empty($_POST)) {
113 $_REQUEST = $_POST;
114 } else {
115 $_REQUEST = array();
116 }
117}
118
119// check parameter syntax
120foreach($vardom as $var => $dom) {
121 if (!isset($_REQUEST[$var])) {
122 $MYREQUEST[$var]=NULL;
123 } else if (!is_array($_REQUEST[$var]) && preg_match($dom.'D',$_REQUEST[$var])) {
124 $MYREQUEST[$var]=$_REQUEST[$var];
125 } else {
126 $MYREQUEST[$var]=$_REQUEST[$var]=NULL;
127 }
128}
129
130// check parameter sematics
131if (empty($MYREQUEST['SCOPE'])) $MYREQUEST['SCOPE']="A";
132if (empty($MYREQUEST['SORT1'])) $MYREQUEST['SORT1']="H";
133if (empty($MYREQUEST['SORT2'])) $MYREQUEST['SORT2']="D";
134if (empty($MYREQUEST['OB'])) $MYREQUEST['OB']=OB_HOST_STATS;
135if (!isset($MYREQUEST['COUNT'])) $MYREQUEST['COUNT']=20;
136if (!isset($scope_list[$MYREQUEST['SCOPE']])) $MYREQUEST['SCOPE']='A';
137
138$MY_SELF=
139 "$PHP_SELF".
140 "?SCOPE=".$MYREQUEST['SCOPE'].
141 "&SORT1=".$MYREQUEST['SORT1'].
142 "&SORT2=".$MYREQUEST['SORT2'].
143 "&COUNT=".$MYREQUEST['COUNT'];
144$MY_SELF_WO_SORT=
145 "$PHP_SELF".
146 "?SCOPE=".$MYREQUEST['SCOPE'].
147 "&COUNT=".$MYREQUEST['COUNT'];
148
149// authentication needed?
150//
151if (!USE_AUTHENTICATION) {
152 $AUTHENTICATED=1;
153} else {
154 $AUTHENTICATED=0;
155 if (ADMIN_PASSWORD!='password' && ($MYREQUEST['LO'] == 1 || isset($_SERVER['PHP_AUTH_USER']))) {
156
157 if (!isset($_SERVER['PHP_AUTH_USER']) ||
158 !isset($_SERVER['PHP_AUTH_PW']) ||
159 $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||
160 $_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {
161 Header("WWW-Authenticate: Basic realm=\"APC Login\"");
162 Header("HTTP/1.0 401 Unauthorized");
163
164 echo <<<EOB
165 <html><body>
166 <h1>Rejected!</h1>
167 <big>Wrong Username or Password!</big><br/>&nbsp;<br/>&nbsp;
168 <big><a href='$PHP_SELF?OB={$MYREQUEST['OB']}'>Continue...</a></big>
169 </body></html>
170EOB;
171 exit;
172
173 } else {
174 $AUTHENTICATED=1;
175 }
176 }
177}
178
179// select cache mode
180if ($AUTHENTICATED && $MYREQUEST['OB'] == OB_USER_CACHE) {
181 $cache_mode='user';
182}
183// clear cache
184if ($AUTHENTICATED && isset($MYREQUEST['CC']) && $MYREQUEST['CC']) {
185 apc_clear_cache($cache_mode);
186}
187
188if ($AUTHENTICATED && !empty($MYREQUEST['DU'])) {
189 apc_delete($MYREQUEST['DU']);
190}
191
192if(!function_exists('apc_cache_info') || !($cache=@apc_cache_info($cache_mode))) {
193 echo "No cache info available. APC does not appear to be running.";
194 exit;
195}
196
197$cache_user = apc_cache_info('user', 1);
198$mem=apc_sma_info();
199if(!$cache['num_hits']) { $cache['num_hits']=1; $time++; } // Avoid division by 0 errors on a cache clear
200
201// don't cache this page
202//
203header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
204header("Cache-Control: post-check=0, pre-check=0", false);
205header("Pragma: no-cache"); // HTTP/1.0
206
207function duration($ts) {
208 global $time;
209 $years = (int)((($time - $ts)/(7*86400))/52.177457);
210 $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400));
211 $weeks = (int)(($rem)/(7*86400));
212 $days = (int)(($rem)/86400) - $weeks*7;
213 $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24;
214 $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60;
215 $str = '';
216 if($years==1) $str .= "$years year, ";
217 if($years>1) $str .= "$years years, ";
218 if($weeks==1) $str .= "$weeks week, ";
219 if($weeks>1) $str .= "$weeks weeks, ";
220 if($days==1) $str .= "$days day,";
221 if($days>1) $str .= "$days days,";
222 if($hours == 1) $str .= " $hours hour and";
223 if($hours>1) $str .= " $hours hours and";
224 if($mins == 1) $str .= " 1 minute";
225 else $str .= " $mins minutes";
226 return $str;
227}
228
229// create graphics
230//
231function graphics_avail() {
232 return extension_loaded('gd');
233}
234if (isset($MYREQUEST['IMG']))
235{
236 if (!graphics_avail()) {
237 exit(0);
238 }
239
240 function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) {
241 $r=$diameter/2;
242 $w=deg2rad((360+$start+($end-$start)/2)%360);
243
244
245 if (function_exists("imagefilledarc")) {
246 // exists only if GD 2.0.1 is avaliable
247 imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE);
248 imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE);
249 imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED);
250 } else {
251 imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2);
252 imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
253 imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
254 imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
255 imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
256 imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2);
257 }
258 if ($text) {
259 if ($placeindex>0) {
260 imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
261 imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
262
263 } else {
264 imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
265 }
266 }
267 }
268
269 function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$text,$placeindex=0) {
270 $r=$diameter/2;
271 $w=deg2rad((360+$start+($end-$start)/2)%360);
272
273 if ($placeindex>0) {
274 imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
275 imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
276
277 } else {
278 imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
279 }
280 }
281
282 function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') {
283 global $col_black;
284 $x1=$x+$w-1;
285 $y1=$y+$h-1;
286
287 imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black);
288 if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2);
289 else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2);
290 imagerectangle($im, $x, $y1, $x1, $y, $color1);
291 if ($text) {
292 if ($placeindex>0) {
293
294 if ($placeindex<16)
295 {
296 $px=5;
297 $py=$placeindex*12+6;
298 imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2);
299 imageline($im,$x,$y+$h/2,$px+90,$py,$color2);
300 imagestring($im,2,$px,$py-6,$text,$color1);
301
302 } else {
303 if ($placeindex<31) {
304 $px=$x+40*2;
305 $py=($placeindex-15)*12+6;
306 } else {
307 $px=$x+40*2+100*intval(($placeindex-15)/15);
308 $py=($placeindex%15)*12+6;
309 }
310 imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2);
311 imageline($im,$x+$w,$y+$h/2,$px,$py,$color2);
312 imagestring($im,2,$px+2,$py-6,$text,$color1);
313 }
314 } else {
315 imagestring($im,4,$x+5,$y1-16,$text,$color1);
316 }
317 }
318 }
319
320
321 $size = GRAPH_SIZE; // image size
322 if ($MYREQUEST['IMG']==3)
323 $image = imagecreate(2*$size+150, $size+10);
324 else
325 $image = imagecreate($size+50, $size+10);
326
327 $col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
328 $col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
329 $col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
330 $col_black = imagecolorallocate($image, 0, 0, 0);
331 imagecolortransparent($image,$col_white);
332
333 switch ($MYREQUEST['IMG']) {
334
335 case 1:
336 $s=$mem['num_seg']*$mem['seg_size'];
337 $a=$mem['avail_mem'];
338 $x=$y=$size/2;
339 $fuzz = 0.000001;
340
341 // This block of code creates the pie chart. It is a lot more complex than you
342 // would expect because we try to visualize any memory fragmentation as well.
343 $angle_from = 0;
344 $string_placement=array();
345 for($i=0; $i<$mem['num_seg']; $i++) {
346 $ptr = 0;
347 $free = $mem['block_lists'][$i];
348 uasort($free, 'block_sort');
349 foreach($free as $block) {
350 if($block['offset']!=$ptr) { // Used block
351 $angle_to = $angle_from+($block['offset']-$ptr)/$s;
352 if(($angle_to+$fuzz)>1) $angle_to = 1;
353 if( ($angle_to*360) - ($angle_from*360) >= 1) {
354 fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red);
355 if (($angle_to-$angle_from)>0.05) {
356 array_push($string_placement, array($angle_from,$angle_to));
357 }
358 }
359 $angle_from = $angle_to;
360 }
361 $angle_to = $angle_from+($block['size'])/$s;
362 if(($angle_to+$fuzz)>1) $angle_to = 1;
363 if( ($angle_to*360) - ($angle_from*360) >= 1) {
364 fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_green);
365 if (($angle_to-$angle_from)>0.05) {
366 array_push($string_placement, array($angle_from,$angle_to));
367 }
368 }
369 $angle_from = $angle_to;
370 $ptr = $block['offset']+$block['size'];
371 }
372 if ($ptr < $mem['seg_size']) { // memory at the end
373 $angle_to = $angle_from + ($mem['seg_size'] - $ptr)/$s;
374 if(($angle_to+$fuzz)>1) $angle_to = 1;
375 fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red);
376 if (($angle_to-$angle_from)>0.05) {
377 array_push($string_placement, array($angle_from,$angle_to));
378 }
379 }
380 }
381 foreach ($string_placement as $angle) {
382 text_arc($image,$x,$y,$size,$angle[0]*360,$angle[1]*360,$col_black,bsize($s*($angle[1]-$angle[0])));
383 }
384 break;
385
386 case 2:
387 $s=$cache['num_hits']+$cache['num_misses'];
388 $a=$cache['num_hits'];
389
390 fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s));
391 fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s));
392 break;
393
394 case 3:
395 $s=$mem['num_seg']*$mem['seg_size'];
396 $a=$mem['avail_mem'];
397 $x=130;
398 $y=1;
399 $j=1;
400
401 // This block of code creates the bar chart. It is a lot more complex than you
402 // would expect because we try to visualize any memory fragmentation as well.
403 for($i=0; $i<$mem['num_seg']; $i++) {
404 $ptr = 0;
405 $free = $mem['block_lists'][$i];
406 uasort($free, 'block_sort');
407 foreach($free as $block) {
408 if($block['offset']!=$ptr) { // Used block
409 $h=(GRAPH_SIZE-5)*($block['offset']-$ptr)/$s;
410 if ($h>0) {
411 $j++;
412 if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($block['offset']-$ptr),$j);
413 else fill_box($image,$x,$y,50,$h,$col_black,$col_red);
414 }
415 $y+=$h;
416 }
417 $h=(GRAPH_SIZE-5)*($block['size'])/$s;
418 if ($h>0) {
419 $j++;
420 if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_green,bsize($block['size']),$j);
421 else fill_box($image,$x,$y,50,$h,$col_black,$col_green);
422 }
423 $y+=$h;
424 $ptr = $block['offset']+$block['size'];
425 }
426 if ($ptr < $mem['seg_size']) { // memory at the end
427 $h = (GRAPH_SIZE-5) * ($mem['seg_size'] - $ptr) / $s;
428 if ($h > 0) {
429 fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($mem['seg_size']-$ptr),$j++);
430 }
431 }
432 }
433 break;
434 case 4:
435 $s=$cache['num_hits']+$cache['num_misses'];
436 $a=$cache['num_hits'];
437
438 fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s));
439 fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s));
440 break;
441
442 }
443 header("Content-type: image/png");
444 imagepng($image);
445 exit;
446}
447
448// pretty printer for byte values
449//
450function bsize($s) {
451 foreach (array('','K','M','G') as $i => $k) {
452 if ($s < 1024) break;
453 $s/=1024;
454 }
455 return sprintf("%5.1f %sBytes",$s,$k);
456}
457
458// sortable table header in "scripts for this host" view
459function sortheader($key,$name,$extra='') {
460 global $MYREQUEST, $MY_SELF_WO_SORT;
461
462 if ($MYREQUEST['SORT1']==$key) {
463 $MYREQUEST['SORT2'] = $MYREQUEST['SORT2']=='A' ? 'D' : 'A';
464 }
465 return "<a class=sortable href=\"$MY_SELF_WO_SORT$extra&SORT1=$key&SORT2=".$MYREQUEST['SORT2']."\">$name</a>";
466
467}
468
469// create menu entry
470function menu_entry($ob,$title) {
471 global $MYREQUEST,$MY_SELF;
472 if ($MYREQUEST['OB']!=$ob) {
473 return "<li><a href=\"$MY_SELF&OB=$ob\">$title</a></li>";
474 } else if (empty($MYREQUEST['SH'])) {
475 return "<li><span class=active>$title</span></li>";
476 } else {
477 return "<li><a class=\"child_active\" href=\"$MY_SELF&OB=$ob\">$title</a></li>";
478 }
479}
480
481function put_login_link($s="Login")
482{
483 global $MY_SELF,$MYREQUEST,$AUTHENTICATED;
484 // needs ADMIN_PASSWORD to be changed!
485 //
486 if (!USE_AUTHENTICATION) {
487 return;
488 } else if (ADMIN_PASSWORD=='password')
489 {
490 print <<<EOB
491 <a href="#" onClick="javascript:alert('You need to set a password at the top of apc.php before this will work!');return false";>$s</a>
492EOB;
493 } else if ($AUTHENTICATED) {
494 print <<<EOB
495 '{$_SERVER['PHP_AUTH_USER']}'&nbsp;logged&nbsp;in!
496EOB;
497 } else{
498 print <<<EOB
499 <a href="$MY_SELF&LO=1&OB={$MYREQUEST['OB']}">$s</a>
500EOB;
501 }
502}
503
504function block_sort($array1, $array2)
505{
506 if ($array1['offset'] > $array2['offset']) {
507 return 1;
508 } else {
509 return -1;
510 }
511}
512
513
514?>
515<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
516<html>
517<head><title>APC INFO <?php echo $host ?></title>
518<style><!--
519body { background:white; font-size:100.01%; margin:0; padding:0; }
520body,p,td,th,input,submit { font-size:0.8em;font-family:arial,helvetica,sans-serif; }
521* html body {font-size:0.8em}
522* html p {font-size:0.8em}
523* html td {font-size:0.8em}
524* html th {font-size:0.8em}
525* html input {font-size:0.8em}
526* html submit {font-size:0.8em}
527td { vertical-align:top }
528a { color:black; font-weight:none; text-decoration:none; }
529a:hover { text-decoration:underline; }
530div.content { padding:1em 1em 1em 1em; position:absolute; width:97%; z-index:100; }
531
532
533div.head div.login {
534 position:absolute;
535 right: 1em;
536 top: 1.2em;
537 color:white;
538 width:6em;
539 }
540div.head div.login a {
541 position:absolute;
542 right: 0em;
543 background:rgb(119,123,180);
544 border:solid rgb(102,102,153) 2px;
545 color:white;
546 font-weight:bold;
547 padding:0.1em 0.5em 0.1em 0.5em;
548 text-decoration:none;
549 }
550div.head div.login a:hover {
551 background:rgb(193,193,244);
552 }
553
554h1.apc { background:rgb(153,153,204); margin:0; padding:0.5em 1em 0.5em 1em; }
555* html h1.apc { margin-bottom:-7px; }
556h1.apc a:hover { text-decoration:none; color:rgb(90,90,90); }
557h1.apc div.logo span.logo {
558 background:rgb(119,123,180);
559 color:black;
560 border-right: solid black 1px;
561 border-bottom: solid black 1px;
562 font-style:italic;
563 font-size:1em;
564 padding-left:1.2em;
565 padding-right:1.2em;
566 text-align:right;
567 }
568h1.apc div.logo span.name { color:white; font-size:0.7em; padding:0 0.8em 0 2em; }
569h1.apc div.nameinfo { color:white; display:inline; font-size:0.4em; margin-left: 3em; }
570h1.apc div.copy { color:black; font-size:0.4em; position:absolute; right:1em; }
571hr.apc {
572 background:white;
573 border-bottom:solid rgb(102,102,153) 1px;
574 border-style:none;
575 border-top:solid rgb(102,102,153) 10px;
576 height:12px;
577 margin:0;
578 margin-top:1px;
579 padding:0;
580}
581
582ol,menu { margin:1em 0 0 0; padding:0.2em; margin-left:1em;}
583ol.menu li { display:inline; margin-right:0.7em; list-style:none; font-size:85%}
584ol.menu a {
585 background:rgb(153,153,204);
586 border:solid rgb(102,102,153) 2px;
587 color:white;
588 font-weight:bold;
589 margin-right:0em;
590 padding:0.1em 0.5em 0.1em 0.5em;
591 text-decoration:none;
592 margin-left: 5px;
593 }
594ol.menu a.child_active {
595 background:rgb(153,153,204);
596 border:solid rgb(102,102,153) 2px;
597 color:white;
598 font-weight:bold;
599 margin-right:0em;
600 padding:0.1em 0.5em 0.1em 0.5em;
601 text-decoration:none;
602 border-left: solid black 5px;
603 margin-left: 0px;
604 }
605ol.menu span.active {
606 background:rgb(153,153,204);
607 border:solid rgb(102,102,153) 2px;
608 color:black;
609 font-weight:bold;
610 margin-right:0em;
611 padding:0.1em 0.5em 0.1em 0.5em;
612 text-decoration:none;
613 border-left: solid black 5px;
614 }
615ol.menu span.inactive {
616 background:rgb(193,193,244);
617 border:solid rgb(182,182,233) 2px;
618 color:white;
619 font-weight:bold;
620 margin-right:0em;
621 padding:0.1em 0.5em 0.1em 0.5em;
622 text-decoration:none;
623 margin-left: 5px;
624 }
625ol.menu a:hover {
626 background:rgb(193,193,244);
627 text-decoration:none;
628 }
629
630
631div.info {
632 background:rgb(204,204,204);
633 border:solid rgb(204,204,204) 1px;
634 margin-bottom:1em;
635 }
636div.info h2 {
637 background:rgb(204,204,204);
638 color:black;
639 font-size:1em;
640 margin:0;
641 padding:0.1em 1em 0.1em 1em;
642 }
643div.info table {
644 border:solid rgb(204,204,204) 1px;
645 border-spacing:0;
646 width:100%;
647 }
648div.info table th {
649 background:rgb(204,204,204);
650 color:white;
651 margin:0;
652 padding:0.1em 1em 0.1em 1em;
653 }
654div.info table th a.sortable { color:black; }
655div.info table tr.tr-0 { background:rgb(238,238,238); }
656div.info table tr.tr-1 { background:rgb(221,221,221); }
657div.info table td { padding:0.3em 1em 0.3em 1em; }
658div.info table td.td-0 { border-right:solid rgb(102,102,153) 1px; white-space:nowrap; }
659div.info table td.td-n { border-right:solid rgb(102,102,153) 1px; }
660div.info table td h3 {
661 color:black;
662 font-size:1.1em;
663 margin-left:-0.3em;
664 }
665
666div.graph { margin-bottom:1em }
667div.graph h2 { background:rgb(204,204,204);; color:black; font-size:1em; margin:0; padding:0.1em 1em 0.1em 1em; }
668div.graph table { border:solid rgb(204,204,204) 1px; color:black; font-weight:normal; width:100%; }
669div.graph table td.td-0 { background:rgb(238,238,238); }
670div.graph table td.td-1 { background:rgb(221,221,221); }
671div.graph table td { padding:0.2em 1em 0.4em 1em; }
672
673div.div1,div.div2 { margin-bottom:1em; width:35em; }
674div.div3 { position:absolute; left:40em; top:1em; width:580px; }
675//div.div3 { position:absolute; left:37em; top:1em; right:1em; }
676
677div.sorting { margin:1.5em 0em 1.5em 2em }
678.center { text-align:center }
679.aright { position:absolute;right:1em }
680.right { text-align:right }
681.ok { color:rgb(0,200,0); font-weight:bold}
682.failed { color:rgb(200,0,0); font-weight:bold}
683
684span.box {
685 border: black solid 1px;
686 border-right:solid black 2px;
687 border-bottom:solid black 2px;
688 padding:0 0.5em 0 0.5em;
689 margin-right:1em;
690}
691span.green { background:#60F060; padding:0 0.5em 0 0.5em}
692span.red { background:#D06030; padding:0 0.5em 0 0.5em }
693
694div.authneeded {
695 background:rgb(238,238,238);
696 border:solid rgb(204,204,204) 1px;
697 color:rgb(200,0,0);
698 font-size:1.2em;
699 font-weight:bold;
700 padding:2em;
701 text-align:center;
702 }
703
704input {
705 background:rgb(153,153,204);
706 border:solid rgb(102,102,153) 2px;
707 color:white;
708 font-weight:bold;
709 margin-right:1em;
710 padding:0.1em 0.5em 0.1em 0.5em;
711 }
712//-->
713</style>
714</head>
715<body>
716<div class="head">
717 <h1 class="apc">
718 <div class="logo"><span class="logo"><a href="http://pecl.php.net/package/APC">APC</a></span></div>
719 <div class="nameinfo">Opcode Cache</div>
720 </h1>
721 <div class="login">
722 <?php put_login_link(); ?>
723 </div>
724 <hr class="apc">
725</div>
726<?php
727
728
729// Display main Menu
730echo <<<EOB
731 <ol class=menu>
732 <li><a href="$MY_SELF&OB={$MYREQUEST['OB']}&SH={$MYREQUEST['SH']}">Refresh Data</a></li>
733EOB;
734echo
735 menu_entry(1,'View Host Stats'),
736 menu_entry(2,'System Cache Entries');
737if ($AUTHENTICATED) {
738 echo menu_entry(4,'Per-Directory Entries');
739}
740echo
741 menu_entry(3,'User Cache Entries'),
742 menu_entry(9,'Version Check');
743
744if ($AUTHENTICATED) {
745 echo <<<EOB
746 <li><a class="aright" href="$MY_SELF&CC=1&OB={$MYREQUEST['OB']}" onClick="javascript:return confirm('Are you sure?');">Clear $cache_mode Cache</a></li>
747EOB;
748}
749echo <<<EOB
750 </ol>
751EOB;
752
753
754// CONTENT
755echo <<<EOB
756 <div class=content>
757EOB;
758
759// MAIN SWITCH STATEMENT
760
761switch ($MYREQUEST['OB']) {
762
763
764
765
766
767// -----------------------------------------------
768// Host Stats
769// -----------------------------------------------
770case OB_HOST_STATS:
771 $mem_size = $mem['num_seg']*$mem['seg_size'];
772 $mem_avail= $mem['avail_mem'];
773 $mem_used = $mem_size-$mem_avail;
774 $seg_size = bsize($mem['seg_size']);
775 $req_rate = sprintf("%.2f",($cache['num_hits']+$cache['num_misses'])/($time-$cache['start_time']));
776 $hit_rate = sprintf("%.2f",($cache['num_hits'])/($time-$cache['start_time']));
777 $miss_rate = sprintf("%.2f",($cache['num_misses'])/($time-$cache['start_time']));
778 $insert_rate = sprintf("%.2f",($cache['num_inserts'])/($time-$cache['start_time']));
779 $req_rate_user = sprintf("%.2f",($cache_user['num_hits']+$cache_user['num_misses'])/($time-$cache_user['start_time']));
780 $hit_rate_user = sprintf("%.2f",($cache_user['num_hits'])/($time-$cache_user['start_time']));
781 $miss_rate_user = sprintf("%.2f",($cache_user['num_misses'])/($time-$cache_user['start_time']));
782 $insert_rate_user = sprintf("%.2f",($cache_user['num_inserts'])/($time-$cache_user['start_time']));
783 $apcversion = phpversion('apc');
784 $phpversion = phpversion();
785 $number_files = $cache['num_entries'];
786 $size_files = bsize($cache['mem_size']);
787 $number_vars = $cache_user['num_entries'];
788 $size_vars = bsize($cache_user['mem_size']);
789 $i=0;
790 echo <<< EOB
791 <div class="info div1"><h2>General Cache Information</h2>
792 <table cellspacing=0><tbody>
793 <tr class=tr-0><td class=td-0>APC Version</td><td>$apcversion</td></tr>
794 <tr class=tr-1><td class=td-0>PHP Version</td><td>$phpversion</td></tr>
795EOB;
796
797 if(!empty($_SERVER['SERVER_NAME']))
798 echo "<tr class=tr-0><td class=td-0>APC Host</td><td>{$_SERVER['SERVER_NAME']} $host</td></tr>\n";
799 if(!empty($_SERVER['SERVER_SOFTWARE']))
800 echo "<tr class=tr-1><td class=td-0>Server Software</td><td>{$_SERVER['SERVER_SOFTWARE']}</td></tr>\n";
801
802 echo <<<EOB
803 <tr class=tr-0><td class=td-0>Shared Memory</td><td>{$mem['num_seg']} Segment(s) with $seg_size
804 <br/> ({$cache['memory_type']} memory, {$cache['locking_type']} locking)
805 </td></tr>
806EOB;
807 echo '<tr class=tr-1><td class=td-0>Start Time</td><td>',date(DATE_FORMAT,$cache['start_time']),'</td></tr>';
808 echo '<tr class=tr-0><td class=td-0>Uptime</td><td>',duration($cache['start_time']),'</td></tr>';
809 echo '<tr class=tr-1><td class=td-0>File Upload Support</td><td>',$cache['file_upload_progress'],'</td></tr>';
810 echo <<<EOB
811 </tbody></table>
812 </div>
813
814 <div class="info div1"><h2>File Cache Information</h2>
815 <table cellspacing=0><tbody>
816 <tr class=tr-0><td class=td-0>Cached Files</td><td>$number_files ($size_files)</td></tr>
817 <tr class=tr-1><td class=td-0>Hits</td><td>{$cache['num_hits']}</td></tr>
818 <tr class=tr-0><td class=td-0>Misses</td><td>{$cache['num_misses']}</td></tr>
819 <tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate cache requests/second</td></tr>
820 <tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate cache requests/second</td></tr>
821 <tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate cache requests/second</td></tr>
822 <tr class=tr-0><td class=td-0>Insert Rate</td><td>$insert_rate cache requests/second</td></tr>
823 <tr class=tr-1><td class=td-0>Cache full count</td><td>{$cache['expunges']}</td></tr>
824 </tbody></table>
825 </div>
826
827 <div class="info div1"><h2>User Cache Information</h2>
828 <table cellspacing=0><tbody>
829 <tr class=tr-0><td class=td-0>Cached Variables</td><td>$number_vars ($size_vars)</td></tr>
830 <tr class=tr-1><td class=td-0>Hits</td><td>{$cache_user['num_hits']}</td></tr>
831 <tr class=tr-0><td class=td-0>Misses</td><td>{$cache_user['num_misses']}</td></tr>
832 <tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate_user cache requests/second</td></tr>
833 <tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate_user cache requests/second</td></tr>
834 <tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate_user cache requests/second</td></tr>
835 <tr class=tr-0><td class=td-0>Insert Rate</td><td>$insert_rate_user cache requests/second</td></tr>
836 <tr class=tr-1><td class=td-0>Cache full count</td><td>{$cache_user['expunges']}</td></tr>
837
838 </tbody></table>
839 </div>
840
841 <div class="info div2"><h2>Runtime Settings</h2><table cellspacing=0><tbody>
842EOB;
843
844 $j = 0;
845 foreach (ini_get_all('apc') as $k => $v) {
846 echo "<tr class=tr-$j><td class=td-0>",$k,"</td><td>",str_replace(',',',<br />',$v['local_value']),"</td></tr>\n";
847 $j = 1 - $j;
848 }
849
850 if($mem['num_seg']>1 || $mem['num_seg']==1 && count($mem['block_lists'][0])>1)
851 $mem_note = "Memory Usage<br /><font size=-2>(multiple slices indicate fragments)</font>";
852 else
853 $mem_note = "Memory Usage";
854
855 echo <<< EOB
856 </tbody></table>
857 </div>
858
859 <div class="graph div3"><h2>Host Status Diagrams</h2>
860 <table cellspacing=0><tbody>
861EOB;
862 $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10);
863 echo <<<EOB
864 <tr>
865 <td class=td-0>$mem_note</td>
866 <td class=td-1>Hits &amp; Misses</td>
867 </tr>
868EOB;
869
870 echo
871 graphics_avail() ?
872 '<tr>'.
873 "<td class=td-0><img alt=\"\" $size src=\"$PHP_SELF?IMG=1&$time\"></td>".
874 "<td class=td-1><img alt=\"\" $size src=\"$PHP_SELF?IMG=2&$time\"></td></tr>\n"
875 : "",
876 '<tr>',
877 '<td class=td-0><span class="green box">&nbsp;</span>Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size),"</td>\n",
878 '<td class=td-1><span class="green box">&nbsp;</span>Hits: ',$cache['num_hits'].sprintf(" (%.1f%%)",$cache['num_hits']*100/($cache['num_hits']+$cache['num_misses'])),"</td>\n",
879 '</tr>',
880 '<tr>',
881 '<td class=td-0><span class="red box">&nbsp;</span>Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size),"</td>\n",
882 '<td class=td-1><span class="red box">&nbsp;</span>Misses: ',$cache['num_misses'].sprintf(" (%.1f%%)",$cache['num_misses']*100/($cache['num_hits']+$cache['num_misses'])),"</td>\n";
883 echo <<< EOB
884 </tr>
885 </tbody></table>
886
887 <br/>
888 <h2>Detailed Memory Usage and Fragmentation</h2>
889 <table cellspacing=0><tbody>
890 <tr>
891 <td class=td-0 colspan=2><br/>
892EOB;
893
894 // Fragementation: (freeseg - 1) / total_seg
895 $nseg = $freeseg = $fragsize = $freetotal = 0;
896 for($i=0; $i<$mem['num_seg']; $i++) {
897 $ptr = 0;
898 foreach($mem['block_lists'][$i] as $block) {
899 if ($block['offset'] != $ptr) {
900 ++$nseg;
901 }
902 $ptr = $block['offset'] + $block['size'];
903 /* Only consider blocks <5M for the fragmentation % */
904 if($block['size']<(5*1024*1024)) $fragsize+=$block['size'];
905 $freetotal+=$block['size'];
906 }
907 $freeseg += count($mem['block_lists'][$i]);
908 }
909
910 if ($freeseg > 1) {
911 $frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize/$freetotal)*100,bsize($fragsize),bsize($freetotal),$freeseg);
912 } else {
913 $frag = "0%";
914 }
915
916 if (graphics_avail()) {
917 $size='width='.(2*GRAPH_SIZE+150).' height='.(GRAPH_SIZE+10);
918 echo <<<EOB
919 <img alt="" $size src="$PHP_SELF?IMG=3&$time">
920EOB;
921 }
922 echo <<<EOB
923 </br>Fragmentation: $frag
924 </td>
925 </tr>
926EOB;
927 if(isset($mem['adist'])) {
928 foreach($mem['adist'] as $i=>$v) {
929 $cur = pow(2,$i); $nxt = pow(2,$i+1)-1;
930 if($i==0) $range = "1";
931 else $range = "$cur - $nxt";
932 echo "<tr><th align=right>$range</th><td align=right>$v</td></tr>\n";
933 }
934 }
935 echo <<<EOB
936 </tbody></table>
937 </div>
938EOB;
939
940 break;
941
942
943// -----------------------------------------------
944// User Cache Entries
945// -----------------------------------------------
946case OB_USER_CACHE:
947 if (!$AUTHENTICATED) {
948 echo '<div class="error">You need to login to see the user values here!<br/>&nbsp;<br/>';
949 put_login_link("Login now!");
950 echo '</div>';
951 break;
952 }
953 $fieldname='info';
954 $fieldheading='User Entry Label';
955 $fieldkey='info';
956
957// -----------------------------------------------
958// System Cache Entries
959// -----------------------------------------------
960case OB_SYS_CACHE:
961 if (!isset($fieldname))
962 {
963 $fieldname='filename';
964 $fieldheading='Script Filename';
965 if(ini_get("apc.stat")) $fieldkey='inode';
966 else $fieldkey='filename';
967 }
968 if (!empty($MYREQUEST['SH']))
969 {
970 echo <<< EOB
971 <div class="info"><table cellspacing=0><tbody>
972 <tr><th>Attribute</th><th>Value</th></tr>
973EOB;
974
975 $m=0;
976 foreach($scope_list as $j => $list) {
977 foreach($cache[$list] as $i => $entry) {
978 if (md5($entry[$fieldkey])!=$MYREQUEST['SH']) continue;
979 foreach($entry as $k => $value) {
980 if (!$AUTHENTICATED) {
981 // hide all path entries if not logged in
982 $value=preg_replace('/^.*(\\/|\\\\)/','<i>&lt;hidden&gt;</i>/',$value);
983 }
984
985 if ($k == "num_hits") {
986 $value=sprintf("%s (%.2f%%)",$value,$value*100/$cache['num_hits']);
987 }
988 if ($k == 'deletion_time') {
989 if(!$entry['deletion_time']) $value = "None";
990 }
991 echo
992 "<tr class=tr-$m>",
993 "<td class=td-0>",ucwords(preg_replace("/_/"," ",$k)),"</td>",
994 "<td class=td-last>",(preg_match("/time/",$k) && $value!='None') ? date(DATE_FORMAT,$value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),"</td>",
995 "</tr>";
996 $m=1-$m;
997 }
998 if($fieldkey=='info') {
999 echo "<tr class=tr-$m><td class=td-0>Stored Value</td><td class=td-last><pre>";
1000 $output = var_export(apc_fetch($entry[$fieldkey]),true);
1001 echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
1002 echo "</pre></td></tr>\n";
1003 }
1004 break;
1005 }
1006 }
1007
1008 echo <<<EOB
1009 </tbody></table>
1010 </div>
1011EOB;
1012 break;
1013 }
1014
1015 $cols=6;
1016 echo <<<EOB
1017 <div class=sorting><form>Scope:
1018 <input type=hidden name=OB value={$MYREQUEST['OB']}>
1019 <select name=SCOPE>
1020EOB;
1021 echo
1022 "<option value=A",$MYREQUEST['SCOPE']=='A' ? " selected":"",">Active</option>",
1023 "<option value=D",$MYREQUEST['SCOPE']=='D' ? " selected":"",">Deleted</option>",
1024 "</select>",
1025 ", Sorting:<select name=SORT1>",
1026 "<option value=H",$MYREQUEST['SORT1']=='H' ? " selected":"",">Hits</option>",
1027 "<option value=Z",$MYREQUEST['SORT1']=='Z' ? " selected":"",">Size</option>",
1028 "<option value=S",$MYREQUEST['SORT1']=='S' ? " selected":"",">$fieldheading</option>",
1029 "<option value=A",$MYREQUEST['SORT1']=='A' ? " selected":"",">Last accessed</option>",
1030 "<option value=M",$MYREQUEST['SORT1']=='M' ? " selected":"",">Last modified</option>",
1031 "<option value=C",$MYREQUEST['SORT1']=='C' ? " selected":"",">Created at</option>",
1032 "<option value=D",$MYREQUEST['SORT1']=='D' ? " selected":"",">Deleted at</option>";
1033 if($fieldname=='info') echo
1034 "<option value=D",$MYREQUEST['SORT1']=='T' ? " selected":"",">Timeout</option>";
1035 echo
1036 '</select>',
1037 '<select name=SORT2>',
1038 '<option value=D',$MYREQUEST['SORT2']=='D' ? ' selected':'','>DESC</option>',
1039 '<option value=A',$MYREQUEST['SORT2']=='A' ? ' selected':'','>ASC</option>',
1040 '</select>',
1041 '<select name=COUNT onChange="form.submit()">',
1042 '<option value=10 ',$MYREQUEST['COUNT']=='10' ? ' selected':'','>Top 10</option>',
1043 '<option value=20 ',$MYREQUEST['COUNT']=='20' ? ' selected':'','>Top 20</option>',
1044 '<option value=50 ',$MYREQUEST['COUNT']=='50' ? ' selected':'','>Top 50</option>',
1045 '<option value=100',$MYREQUEST['COUNT']=='100'? ' selected':'','>Top 100</option>',
1046 '<option value=150',$MYREQUEST['COUNT']=='150'? ' selected':'','>Top 150</option>',
1047 '<option value=200',$MYREQUEST['COUNT']=='200'? ' selected':'','>Top 200</option>',
1048 '<option value=500',$MYREQUEST['COUNT']=='500'? ' selected':'','>Top 500</option>',
1049 '<option value=0 ',$MYREQUEST['COUNT']=='0' ? ' selected':'','>All</option>',
1050 '</select>',
1051 '&nbsp; Search: <input name=SEARCH value="',$MYREQUEST['SEARCH'],'" type=text size=25/>',
1052 '&nbsp;<input type=submit value="GO!">',
1053 '</form></div>';
1054
1055 if (isset($MYREQUEST['SEARCH'])) {
1056 // Don't use preg_quote because we want the user to be able to specify a
1057 // regular expression subpattern.
1058 $MYREQUEST['SEARCH'] = '/'.str_replace('/', '\\/', $MYREQUEST['SEARCH']).'/i';
1059 if (preg_match($MYREQUEST['SEARCH'], 'test') === false) {
1060 echo '<div class="error">Error: enter a valid regular expression as a search query.</div>';
1061 break;
1062 }
1063 }
1064
1065 echo
1066 '<div class="info"><table cellspacing=0><tbody>',
1067 '<tr>',
1068 '<th>',sortheader('S',$fieldheading, "&OB=".$MYREQUEST['OB']),'</th>',
1069 '<th>',sortheader('H','Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1070 '<th>',sortheader('Z','Size', "&OB=".$MYREQUEST['OB']),'</th>',
1071 '<th>',sortheader('A','Last accessed',"&OB=".$MYREQUEST['OB']),'</th>',
1072 '<th>',sortheader('M','Last modified',"&OB=".$MYREQUEST['OB']),'</th>',
1073 '<th>',sortheader('C','Created at', "&OB=".$MYREQUEST['OB']),'</th>';
1074
1075 if($fieldname=='info') {
1076 $cols+=2;
1077 echo '<th>',sortheader('T','Timeout',"&OB=".$MYREQUEST['OB']),'</th>';
1078 }
1079 echo '<th>',sortheader('D','Deleted at',"&OB=".$MYREQUEST['OB']),'</th></tr>';
1080
1081 // builds list with alpha numeric sortable keys
1082 //
1083 $list = array();
1084 foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $i => $entry) {
1085 switch($MYREQUEST['SORT1']) {
1086 case 'A': $k=sprintf('%015d-',$entry['access_time']); break;
1087 case 'H': $k=sprintf('%015d-',$entry['num_hits']); break;
1088 case 'Z': $k=sprintf('%015d-',$entry['mem_size']); break;
1089 case 'M': $k=sprintf('%015d-',$entry['mtime']); break;
1090 case 'C': $k=sprintf('%015d-',$entry['creation_time']); break;
1091 case 'T': $k=sprintf('%015d-',$entry['ttl']); break;
1092 case 'D': $k=sprintf('%015d-',$entry['deletion_time']); break;
1093 case 'S': $k=''; break;
1094 }
1095 if (!$AUTHENTICATED) {
1096 // hide all path entries if not logged in
1097 $list[$k.$entry[$fieldname]]=preg_replace('/^.*(\\/|\\\\)/','*hidden*/',$entry);
1098 } else {
1099 $list[$k.$entry[$fieldname]]=$entry;
1100 }
1101 }
1102
1103 if ($list) {
1104
1105 // sort list
1106 //
1107 switch ($MYREQUEST['SORT2']) {
1108 case "A": krsort($list); break;
1109 case "D": ksort($list); break;
1110 }
1111
1112 // output list
1113 $i=0;
1114 foreach($list as $k => $entry) {
1115 if(!$MYREQUEST['SEARCH'] || preg_match($MYREQUEST['SEARCH'], $entry[$fieldname]) != 0) {
1116 $field_value = htmlentities(strip_tags($entry[$fieldname],''), ENT_QUOTES, 'UTF-8');
1117 echo
1118 '<tr class=tr-',$i%2,'>',
1119 "<td class=td-0><a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&SH=",md5($entry[$fieldkey]),"\">",$field_value,'</a></td>',
1120 '<td class="td-n center">',$entry['num_hits'],'</td>',
1121 '<td class="td-n right">',$entry['mem_size'],'</td>',
1122 '<td class="td-n center">',date(DATE_FORMAT,$entry['access_time']),'</td>',
1123 '<td class="td-n center">',date(DATE_FORMAT,$entry['mtime']),'</td>',
1124 '<td class="td-n center">',date(DATE_FORMAT,$entry['creation_time']),'</td>';
1125
1126 if($fieldname=='info') {
1127 if($entry['ttl'])
1128 echo '<td class="td-n center">'.$entry['ttl'].' seconds</td>';
1129 else
1130 echo '<td class="td-n center">None</td>';
1131 }
1132 if ($entry['deletion_time']) {
1133
1134 echo '<td class="td-last center">', date(DATE_FORMAT,$entry['deletion_time']), '</td>';
1135 } else if ($MYREQUEST['OB'] == OB_USER_CACHE) {
1136
1137 echo '<td class="td-last center">';
1138 echo '[<a href="', $MY_SELF, '&OB=', $MYREQUEST['OB'], '&DU=', urlencode($entry[$fieldkey]), '">Delete Now</a>]';
1139 echo '</td>';
1140 } else {
1141 echo '<td class="td-last center"> &nbsp; </td>';
1142 }
1143 echo '</tr>';
1144 $i++;
1145 if ($i == $MYREQUEST['COUNT'])
1146 break;
1147 }
1148 }
1149
1150 } else {
1151 echo '<tr class=tr-0><td class="center" colspan=',$cols,'><i>No data</i></td></tr>';
1152 }
1153 echo <<< EOB
1154 </tbody></table>
1155EOB;
1156
1157 if ($list && $i < count($list)) {
1158 echo "<a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&COUNT=0\"><i>",count($list)-$i,' more available...</i></a>';
1159 }
1160
1161 echo <<< EOB
1162 </div>
1163EOB;
1164 break;
1165
1166
1167// -----------------------------------------------
1168// Per-Directory System Cache Entries
1169// -----------------------------------------------
1170case OB_SYS_CACHE_DIR:
1171 if (!$AUTHENTICATED) {
1172 break;
1173 }
1174
1175 echo <<<EOB
1176 <div class=sorting><form>Scope:
1177 <input type=hidden name=OB value={$MYREQUEST['OB']}>
1178 <select name=SCOPE>
1179EOB;
1180 echo
1181 "<option value=A",$MYREQUEST['SCOPE']=='A' ? " selected":"",">Active</option>",
1182 "<option value=D",$MYREQUEST['SCOPE']=='D' ? " selected":"",">Deleted</option>",
1183 "</select>",
1184 ", Sorting:<select name=SORT1>",
1185 "<option value=H",$MYREQUEST['SORT1']=='H' ? " selected":"",">Total Hits</option>",
1186 "<option value=Z",$MYREQUEST['SORT1']=='Z' ? " selected":"",">Total Size</option>",
1187 "<option value=T",$MYREQUEST['SORT1']=='T' ? " selected":"",">Number of Files</option>",
1188 "<option value=S",$MYREQUEST['SORT1']=='S' ? " selected":"",">Directory Name</option>",
1189 "<option value=A",$MYREQUEST['SORT1']=='A' ? " selected":"",">Avg. Size</option>",
1190 "<option value=C",$MYREQUEST['SORT1']=='C' ? " selected":"",">Avg. Hits</option>",
1191 '</select>',
1192 '<select name=SORT2>',
1193 '<option value=D',$MYREQUEST['SORT2']=='D' ? ' selected':'','>DESC</option>',
1194 '<option value=A',$MYREQUEST['SORT2']=='A' ? ' selected':'','>ASC</option>',
1195 '</select>',
1196 '<select name=COUNT onChange="form.submit()">',
1197 '<option value=10 ',$MYREQUEST['COUNT']=='10' ? ' selected':'','>Top 10</option>',
1198 '<option value=20 ',$MYREQUEST['COUNT']=='20' ? ' selected':'','>Top 20</option>',
1199 '<option value=50 ',$MYREQUEST['COUNT']=='50' ? ' selected':'','>Top 50</option>',
1200 '<option value=100',$MYREQUEST['COUNT']=='100'? ' selected':'','>Top 100</option>',
1201 '<option value=150',$MYREQUEST['COUNT']=='150'? ' selected':'','>Top 150</option>',
1202 '<option value=200',$MYREQUEST['COUNT']=='200'? ' selected':'','>Top 200</option>',
1203 '<option value=500',$MYREQUEST['COUNT']=='500'? ' selected':'','>Top 500</option>',
1204 '<option value=0 ',$MYREQUEST['COUNT']=='0' ? ' selected':'','>All</option>',
1205 '</select>',
1206 ", Group By Dir Level:<select name=AGGR>",
1207 "<option value='' selected>None</option>";
1208 for ($i = 1; $i < 10; $i++)
1209 echo "<option value=$i",$MYREQUEST['AGGR']==$i ? " selected":"",">$i</option>";
1210 echo '</select>',
1211 '&nbsp;<input type=submit value="GO!">',
1212 '</form></div>',
1213
1214 '<div class="info"><table cellspacing=0><tbody>',
1215 '<tr>',
1216 '<th>',sortheader('S','Directory Name', "&OB=".$MYREQUEST['OB']),'</th>',
1217 '<th>',sortheader('T','Number of Files',"&OB=".$MYREQUEST['OB']),'</th>',
1218 '<th>',sortheader('H','Total Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1219 '<th>',sortheader('Z','Total Size', "&OB=".$MYREQUEST['OB']),'</th>',
1220 '<th>',sortheader('C','Avg. Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1221 '<th>',sortheader('A','Avg. Size', "&OB=".$MYREQUEST['OB']),'</th>',
1222 '</tr>';
1223
1224 // builds list with alpha numeric sortable keys
1225 //
1226 $tmp = $list = array();
1227 foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $entry) {
1228 $n = dirname($entry['filename']);
1229 if ($MYREQUEST['AGGR'] > 0) {
1230 $n = preg_replace("!^(/?(?:[^/\\\\]+[/\\\\]){".($MYREQUEST['AGGR']-1)."}[^/\\\\]*).*!", "$1", $n);
1231 }
1232 if (!isset($tmp[$n])) {
1233 $tmp[$n] = array('hits'=>0,'size'=>0,'ents'=>0);
1234 }
1235 $tmp[$n]['hits'] += $entry['num_hits'];
1236 $tmp[$n]['size'] += $entry['mem_size'];
1237 ++$tmp[$n]['ents'];
1238 }
1239
1240 foreach ($tmp as $k => $v) {
1241 switch($MYREQUEST['SORT1']) {
1242 case 'A': $kn=sprintf('%015d-',$v['size'] / $v['ents']);break;
1243 case 'T': $kn=sprintf('%015d-',$v['ents']); break;
1244 case 'H': $kn=sprintf('%015d-',$v['hits']); break;
1245 case 'Z': $kn=sprintf('%015d-',$v['size']); break;
1246 case 'C': $kn=sprintf('%015d-',$v['hits'] / $v['ents']);break;
1247 case 'S': $kn = $k; break;
1248 }
1249 $list[$kn.$k] = array($k, $v['ents'], $v['hits'], $v['size']);
1250 }
1251
1252 if ($list) {
1253
1254 // sort list
1255 //
1256 switch ($MYREQUEST['SORT2']) {
1257 case "A": krsort($list); break;
1258 case "D": ksort($list); break;
1259 }
1260
1261 // output list
1262 $i = 0;
1263 foreach($list as $entry) {
1264 echo
1265 '<tr class=tr-',$i%2,'>',
1266 "<td class=td-0>",$entry[0],'</a></td>',
1267 '<td class="td-n center">',$entry[1],'</td>',
1268 '<td class="td-n center">',$entry[2],'</td>',
1269 '<td class="td-n center">',$entry[3],'</td>',
1270 '<td class="td-n center">',round($entry[2] / $entry[1]),'</td>',
1271 '<td class="td-n center">',round($entry[3] / $entry[1]),'</td>',
1272 '</tr>';
1273
1274 if (++$i == $MYREQUEST['COUNT']) break;
1275 }
1276
1277 } else {
1278 echo '<tr class=tr-0><td class="center" colspan=6><i>No data</i></td></tr>';
1279 }
1280 echo <<< EOB
1281 </tbody></table>
1282EOB;
1283
1284 if ($list && $i < count($list)) {
1285 echo "<a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&COUNT=0\"><i>",count($list)-$i,' more available...</i></a>';
1286 }
1287
1288 echo <<< EOB
1289 </div>
1290EOB;
1291 break;
1292
1293// -----------------------------------------------
1294// Version check
1295// -----------------------------------------------
1296case OB_VERSION_CHECK:
1297 echo <<<EOB
1298 <div class="info"><h2>APC Version Information</h2>
1299 <table cellspacing=0><tbody>
1300 <tr>
1301 <th></th>
1302 </tr>
1303EOB;
1304 if (defined('PROXY')) {
1305 $ctxt = stream_context_create( array( 'http' => array( 'proxy' => PROXY, 'request_fulluri' => True ) ) );
1306 $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss", False, $ctxt);
1307 } else {
1308 $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss");
1309 }
1310 if (!$rss) {
1311 echo '<tr class="td-last center"><td>Unable to fetch version information.</td></tr>';
1312 } else {
1313 $apcversion = phpversion('apc');
1314
1315 preg_match('!<title>APC ([0-9.]+)</title>!', $rss, $match);
1316 echo '<tr class="tr-0 center"><td>';
1317 if (version_compare($apcversion, $match[1], '>=')) {
1318 echo '<div class="ok">You are running the latest version of APC ('.$apcversion.')</div>';
1319 $i = 3;
1320 } else {
1321 echo '<div class="failed">You are running an older version of APC ('.$apcversion.'),
1322 newer version '.$match[1].' is available at <a href="http://pecl.php.net/package/APC/'.$match[1].'">
1323 http://pecl.php.net/package/APC/'.$match[1].'</a>
1324 </div>';
1325 $i = -1;
1326 }
1327 echo '</td></tr>';
1328 echo '<tr class="tr-0"><td><h3>Change Log:</h3><br/>';
1329
1330 preg_match_all('!<(title|description)>([^<]+)</\\1>!', $rss, $match);
1331 next($match[2]); next($match[2]);
1332
1333 while (list(,$v) = each($match[2])) {
1334 list(,$ver) = explode(' ', $v, 2);
1335 if ($i < 0 && version_compare($apcversion, $ver, '>=')) {
1336 break;
1337 } else if (!$i--) {
1338 break;
1339 }
1340 echo "<b><a href=\"http://pecl.php.net/package/APC/$ver\">".htmlspecialchars($v, ENT_QUOTES, 'UTF-8')."</a></b><br><blockquote>";
1341 echo nl2br(htmlspecialchars(current($match[2]), ENT_QUOTES, 'UTF-8'))."</blockquote>";
1342 next($match[2]);
1343 }
1344 echo '</td></tr>';
1345 }
1346 echo <<< EOB
1347 </tbody></table>
1348 </div>
1349EOB;
1350 break;
1351
1352}
1353
1354echo <<< EOB
1355 </div>
1356EOB;
1357
1358?>
1359
1360<!-- <?php echo "\nBased on APCGUI By R.Becker\n$VERSION\n"?> -->
1361</body>
1362</html>
1363
1364
01365
=== added file 'files/_debug/info.php'
--- files/_debug/info.php 1970-01-01 00:00:00 +0000
+++ files/_debug/info.php 2012-08-21 13:22:29 +0000
@@ -0,0 +1,3 @@
1<?php
2
3phpinfo();
04
=== added directory 'files/charm'
=== added directory 'files/charm/nginx'
=== added file 'files/charm/nginx/etc_nginx_nginx.conf'
--- files/charm/nginx/etc_nginx_nginx.conf 1970-01-01 00:00:00 +0000
+++ files/charm/nginx/etc_nginx_nginx.conf 2012-08-21 13:22:29 +0000
@@ -0,0 +1,58 @@
1user www-data;
2worker_processes 4;
3pid /var/run/nginx.pid;
4
5events {
6 worker_connections 8192;
7 multi_accept on;
8 use epoll;
9}
10
11http {
12 ##
13 # Reverse
14 ##
15 set_real_ip_from 0.0.0.0/0;
16 real_ip_header X-Forwarded-For;
17 add_header X-UA-Compatible "IE=Edge,chrome=1";
18
19 ##
20 # Rockin Defaults
21 ##
22 sendfile on;
23 tcp_nopush on;
24 tcp_nodelay on;
25 keepalive_timeout 5;
26 types_hash_max_size 2048;
27 server_names_hash_bucket_size 64;
28 server_name_in_redirect off;
29 client_max_body_size 512k;
30 include /etc/nginx/mime.types;
31 default_type application/octet-stream;
32 index index.php index.html
33
34 ##
35 # Logging Settings
36 ##
37 access_log /var/log/nginx/access.log;
38 error_log /var/log/nginx/error.log;
39
40 ##
41 # Gzip Settings
42 ##
43 gzip on;
44 gzip_disable "msie6";
45 gzip_vary on;
46 gzip_static on;
47 gzip_proxied any;
48 gzip_comp_level 4;
49 gzip_buffers 16 8k;
50 gzip_http_version 1.1;
51 gzip_types text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
52
53 ##
54 # Virtual Host Configs
55 ##
56 include /etc/nginx/conf.d/*.conf;
57 include /etc/nginx/sites-enabled/*;
58}
059
=== added file 'files/charm/nginx/etc_nginx_sites-enabled_loadbalancer'
--- files/charm/nginx/etc_nginx_sites-enabled_loadbalancer 1970-01-01 00:00:00 +0000
+++ files/charm/nginx/etc_nginx_sites-enabled_loadbalancer 2012-08-21 13:22:29 +0000
@@ -0,0 +1,51 @@
1# Default before peer relation is handled
2proxy_cache_path /mnt/ramdisk/proxy-cache levels=1:2 keys_zone=proxycache:5m max_size=1000m;
3
4upstream backend {
5 server 127.0.0.1:8080;
6}
7
8server {
9 listen 80 default;
10 server_name _;
11 location / {
12 set $no_cache "";
13
14 if ($request_method !~ ^(GET|HEAD)$) {
15 set $no_cache "1";
16 }
17
18 if ($no_cache = "1") {
19 add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
20 add_header X-Microcachable "0";
21 }
22
23 if ($http_cookie ~* "_mcnc") {
24 set $no_cache "1";
25 }
26
27 proxy_no_cache $no_cache;
28 proxy_cache_bypass $no_cache;
29
30 proxy_redirect http://backend /;
31 proxy_pass http://backend;
32 proxy_cache proxycache;
33 proxy_cache_key $scheme$host$request_method$request_uri;
34 proxy_cache_valid 200 60s;
35 proxy_cache_use_stale updating;
36
37 proxy_set_header Host $host;
38 proxy_set_header X-Real-IP $remote_addr;
39 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
40
41 proxy_max_temp_file_size 1M;
42
43 # Custom logging
44 log_format custom '$remote_addr - $remote_user [$time_local] '
45 '"$request" $status $body_bytes_sent '
46 '"$http_referer" "$http_user_agent" nocache:$no_cache';
47 access_log /mnt/logs/microcache.log custom;
48
49 }
50}
51
052
=== added file 'files/charm/nginx/etc_nginx_sites-enabled_wordpress'
--- files/charm/nginx/etc_nginx_sites-enabled_wordpress 1970-01-01 00:00:00 +0000
+++ files/charm/nginx/etc_nginx_sites-enabled_wordpress 2012-08-21 13:22:29 +0000
@@ -0,0 +1,72 @@
1fastcgi_cache_path /mnt/ramdisk/phpfpm-cache levels=1:2 keys_zone=microcache:5M max_size=1G inactive=2h;
2
3server {
4 listen 8080 default;
5 server_name _;
6 access_log /mnt/access.log;
7 error_log /mnt/error.log;
8 root /var/www/;
9 index index.php index.html;
10 client_max_body_size 10m;
11 client_body_buffer_size 128k;
12 client_header_buffer_size 64k;
13
14 location = /favicon.ico {
15 allow all;
16 log_not_found off;
17 access_log off;
18 }
19
20 location = /robots.txt {
21 allow all;
22 log_not_found off;
23 access_log off;
24 }
25
26 location ~* \.(js|css|png|jpg|jpeg|gif|ico|zip|gz|tar)$ {
27 log_not_found off;
28 access_log off;
29 expires max;
30 }
31
32 location / {
33 # try_files "${document_root}/wp-content/cache/supercache/${host}/${uri}index.html" "${uri}/" "${uri}/index.html" $uri @rewrite;
34 try_files $uri $uri/ /index.php?q=$uri&$args;
35 }
36
37 location ~ /\. {
38 access_log off;
39 log_not_found off;
40 deny all;
41 }
42
43 location ~* /(fpm.www.status|fpm.www.ping)$ {
44 include /etc/nginx/fastcgi_params;
45 fastcgi_pass unix:/var/run/php5-fpm.sock;
46 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
47 fastcgi_param SCRIPT_NAME $fastcgi_script_name;
48 fastcgi_param QUERY_STRING $args;
49 }
50
51 location ~ \.php$ {
52 try_files $uri @rewrite;
53 include /etc/nginx/fastcgi_params;
54 fastcgi_pass unix:/var/run/php5-fpm.sock;
55 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
56 fastcgi_param SCRIPT_NAME $fastcgi_script_name;
57 fastcgi_param QUERY_STRING $args;
58 }
59
60 location @rewrite {
61 index index.php;
62 include /etc/nginx/fastcgi_params;
63 fastcgi_pass unix:/var/run/php5-fpm.sock;
64 fastcgi_buffers 256 4k;
65 fastcgi_intercept_errors on;
66 fastcgi_hide_header 'X-Generator';
67 fastcgi_read_timeout 14400;
68 fastcgi_param SCRIPT_FILENAME $document_root/index.php;
69 fastcgi_param SCRIPT_NAME /index.php;
70 fastcgi_param QUERY_STRING $args;
71 }
72}
073
=== added directory 'files/charm/php'
=== added file 'files/charm/php/php5-fpm_pool.d_www.conf'
--- files/charm/php/php5-fpm_pool.d_www.conf 1970-01-01 00:00:00 +0000
+++ files/charm/php/php5-fpm_pool.d_www.conf 2012-08-21 13:22:29 +0000
@@ -0,0 +1,41 @@
1; -------------------------------------------------------------------- ;
2; first pool
3; -------------------------------------------------------------------- ;
4
5[www]
6user = www-data
7group = www-data
8
9listen = /var/run/php5-fpm.sock
10
11request_slowlog_timeout = 10s
12slowlog = /mnt/logs/php-fpm/$pool.slowlog.log
13
14pm = dynamic
15pm.max_children = 100
16pm.start_servers = 15
17pm.min_spare_servers = 4
18pm.max_spare_servers = 15
19pm.max_requests = 5000
20listen.backlog = -1
21
22; http://url/status
23; http://url/status?json
24; http://url/status?html
25pm.status_path = /fpm.$pool.status
26ping.path = /fpm.$pool.ping
27ping.response = "pong. $pool OK"
28
29request_terminate_timeout = 120s
30rlimit_files = 131072
31rlimit_core = unlimited
32catch_workers_output = no
33
34; this is untested will do this on a later update
35
36env[HOSTNAME] = $HOSTNAME
37env[PATH] = $PATH
38env[TMP] = /mnt/tmp
39env[TMPDIR] = /mnt/tmp
40env[TEMP] = /mnt/tmp
41env[OHSO_POOL] = $pool
042
=== added file 'files/charm/php/php5_conf.d_apc.ini'
--- files/charm/php/php5_conf.d_apc.ini 1970-01-01 00:00:00 +0000
+++ files/charm/php/php5_conf.d_apc.ini 2012-08-21 13:22:29 +0000
@@ -0,0 +1,13 @@
1extension=apc.so
2apc.enable=1
3apc.shm_size = 1024M
4apc.stat_ctime = 0
5apc.stat = 0
6apc.max_file_size = 2M
7apc.write_lock = true
8apc.mmap = /mnt/tmp/apc.XXXXXX
9apc.ttl = 0
10apc.user_ttl = 7200
11apc.gc_ttl = 7200
12apc.use_request_time = 1
13apc.file_md5 = 1
014
=== added file 'hooks/config-changed'
--- hooks/config-changed 1970-01-01 00:00:00 +0000
+++ hooks/config-changed 2012-08-21 13:22:29 +0000
@@ -0,0 +1,54 @@
1#!/bin/bash
2
3set -ue
4
5source inc/common
6
7tuning_level=`config-get tuning`
8# Make it lower case
9tuning_level=${tuning_level,,}
10
11wp_content_repo=`config-get wp-content`
12expose_info=`config-get debug`
13expose_info=${expose_info,,}
14
15if [ ! -f $config_file_path ]; then
16 juju-log "Nothing to configure, since nothing is installed"
17 exit 0
18fi
19
20juju-log "Show details? $expose_info"
21
22if [ "$expose_info" == "yes" ]; then
23 rsync -az files/_debug $wp_install_path/
24else
25 rm -rf $wp_install_path/_debug
26fi
27
28juju-log "I will be using this tuning level: $tuning_level"
29
30if [ "$tuning_level" == "optimized" ]; then
31 # First and foremost, we need to disable the ability to edit
32 # themes and upload/update plugins. This breaks a scale-out
33 # environment. It's sad but true. If you want to update a plugin
34 # install a theme, etc; take a look at the README.
35 make_optimized
36elif [ "$tuning_level" == "single" ]; then
37 # We need to prepare an NFS mount, because someone is probably
38 # going to try to scale out. We also need to vamp up caching.
39 make_single
40elif [ "$tuning_level" == "bare" ]; then
41 # Okay, you know what you're doing. You're probably going to
42 # use Gluster to stream-line your files, so you don't need to
43 # disable anything. We trust you to do what you need to.
44 make_bare
45else
46 juju-log "Not sure about that tuning level."
47 exit 1
48fi
49
50do_vcs $wp_content_repo
51
52chown -R www-data.www-data $wp_install_path
53
54. hooks/restart
055
=== added symlink 'hooks/db-relation-broken'
=== target is u'db-relation-departed'
=== modified file 'hooks/db-relation-changed'
--- hooks/db-relation-changed 2012-06-18 20:28:16 +0000
+++ hooks/db-relation-changed 2012-08-21 13:22:29 +0000
@@ -1,5 +1,6 @@
1#!/bin/bash1#!/bin/bash
22
3<<<<<<< TREE
3set -eu # -x for verbose logging to juju debug-log4set -eu # -x for verbose logging to juju debug-log
45
5UPLOAD_PATH="/var/www/wp-uploads"6UPLOAD_PATH="/var/www/wp-uploads"
@@ -9,24 +10,31 @@
910
10# Check we haven't already been setup.11# Check we haven't already been setup.
11config_file_path="/etc/wordpress/config-default.php"12config_file_path="/etc/wordpress/config-default.php"
1213=======
13# Get the database settings; if not set, wait for this hook to be14set -eu
14# invoked again15
16juju-log "We've got a db"
17
18source inc/common
19
20if [ -f "$config_file_path" ]; then
21 # No longer to quietly in to that good night. Update the wp-config file with new DB values
22 juju-log "WordPress is already setup, just silently going away"
23 exit 0
24fi
25>>>>>>> MERGE-SOURCE
26
15database=`relation-get database`27database=`relation-get database`
16if [ -z "$database" ] ; then
17 exit 0 # wait for future handshake from database service unit
18fi
19
20# Our protocol on this interface ensures that all or none of the
21# settings are set. But we can verify the setting or the values if
22# more error checking if desired.
23user=`relation-get user`28user=`relation-get user`
24password=`relation-get password`29password=`relation-get password`
25host=`relation-get private-address`30host=`relation-get private-address`
2631
27# Create an internal secret key for wordpress; this is unrelated to32if [ -z "$database" ] ; then
28# the password generated for the admin user of wordpress33 exit 0
34fi
35
29secret_key=`pwgen 10 1`36secret_key=`pwgen 10 1`
37<<<<<<< TREE
3038
31juju-log "Creating appropriate upload paths and directories"39juju-log "Creating appropriate upload paths and directories"
32# Setup appropriate upload paths and directories40# Setup appropriate upload paths and directories
@@ -37,10 +45,39 @@
37chmod 0744 $UPLOAD_PATH45chmod 0744 $UPLOAD_PATH
38chmod 0770 "$UPLOAD_PATH/$hostname"46chmod 0770 "$UPLOAD_PATH/$hostname"
39chown -R root:www-data "/var/www/$hostname/wp-content"47chown -R root:www-data "/var/www/$hostname/wp-content"
48=======
49echo $secret_key > wp_secret
50source "/usr/share/charm-helper/sh/net.sh"
51
52##
53# OH COME ON WORDPRESS.ORG NO VALID SSL CERTIFICATE?
54# Psh, whatever, plain-text for now. #blamenacin
55# https://twitter.com/#!/marcoceppi/status/181570187655520260
56# I was wrong, we need to use something other than WGET, like cURL
57LATEST_WORDPRESS="http://wordpress.org/latest.tar.gz"
58DOWNLOAD=`ch_get_file "$LATEST_WORDPRESS" "http://wordpress.org/latest.tar.gz.sha1"`
59##
60
61if [ ! -f "$DOWNLOAD" ] || [ -z "$DOWNLOAD" ]; then
62 juju-log "Failed to retrieve $LATEST_WORDPRESS"
63 exit 1
64fi
65
66juju-log "Extract ALL THE FILES"
67tar -xzf $DOWNLOAD
68
69mkdir -p $wp_install_path
70
71juju-log "Move them in to place, but just 'drop' them in place."
72rsync -az wordpress/ $wp_install_path
73
74juju-log "Clean up"
75rm -rf wordpress
76>>>>>>> MERGE-SOURCE
4077
41juju-log "Writing wordpress config file $config_file_path"78juju-log "Writing wordpress config file $config_file_path"
42# Write the wordpress config79# Write the wordpress config
43cat > $config_file_path <<EOF80cat > $config_info_path <<EOF
44<?php81<?php
45define('DB_NAME', '$database');82define('DB_NAME', '$database');
46define('DB_USER', '$user');83define('DB_USER', '$user');
@@ -48,49 +85,60 @@
48define('DB_HOST', '$host');85define('DB_HOST', '$host');
49define('SECRET_KEY', '$secret_key');86define('SECRET_KEY', '$secret_key');
5087
51#This will disable the update notification.88define('WP_CACHE', true);
52define('WP_CORE_UPDATE', false);89
90/*
91define('AUTH_KEY', '$secret_key');
92define('SECURE_AUTH_KEY', '$secret_key');
93define('LOGGED_IN_KEY', '$secret_key');
94define('NONCE_KEY', '$secret_key');
95*/
5396
54\$table_prefix = 'wp_';97\$table_prefix = 'wp_';
55\$server = '$host';98
56\$loginsql = '$user';99EOF
57\$passsql = '$password';100
58\$base = '$database';101cat > $config_file_path <<EOF
59\$upload_path = '/var/www/wp-uploads/$hostname';102<?php
60\$upload_url_path = 'http://$hostname/wp-uploads';103
61?>104/* That's all, stop editing! Happy blogging. */
62EOF105
106/** Absolute path to the WordPress directory. */
107if ( !defined('ABSPATH') )
108 define('ABSPATH', dirname(__FILE__) . '/');
109
110/** Pull in the config information */
111require_once(ABSPATH . 'wp-info.php');
112require_once(ABSPATH . 'wp-overrides.php');
113
114/** Sets up WordPress vars and included files. */
115require_once(ABSPATH . 'wp-settings.php');
116
117remove_filter('template_redirect', 'redirect_canonical');
118
119EOF
120
63chmod 0644 $config_file_path121chmod 0644 $config_file_path
64122touch $config_override_path
65# Write the apache config123
66# XXX a future branch will change this to use augtool
67apache_config_file_path="/etc/apache2/sites-available/$hostname"
68juju-log "Writing apache config file $apache_config_file_path"
69cat > $apache_config_file_path <<EOF
70<VirtualHost *:80>
71 ServerName $hostname
72 DocumentRoot /var/www/$hostname
73 Options All
74 ErrorLog /var/log/apache2/wp-error.log
75 TransferLog /var/log/apache2/wp-access.log
76 # Store uploads in /var/www/wp-uploads/$0
77 RewriteEngine On
78 RewriteRule ^/wp-uploads/(.*)$ /var/www/wp-uploads/%%{HTTP_HOST}/\$1
79</VirtualHost>
80EOF
81chmod 0644 $apache_config_file_path
82
83# Configure apache
84juju-log "Enabling apache modules: rewrite, vhost_alias"
85a2enmod rewrite
86a2enmod vhost_alias
87juju-log "Enabling apache site: $hostname"124juju-log "Enabling apache site: $hostname"
125<<<<<<< TREE
88a2ensite $hostname126a2ensite $hostname
89a2dissite default127a2dissite default
90128
91# Restart apache129# Restart apache
92juju-log "Restarting apache2 service"130juju-log "Restarting apache2 service"
93/etc/init.d/apache2 restart131/etc/init.d/apache2 restart
132=======
133
134chown -R www-data.www-data $wp_install_path
135
136. hooks/config-changed
137
138# Restart nginx
139juju-log "Restarting nginx service"
140service nginx restart
141>>>>>>> MERGE-SOURCE
94142
95# Make it publicly visible, once the wordpress service is exposed143# Make it publicly visible, once the wordpress service is exposed
96open-port 80/tcp144open-port 80/tcp
97145
=== added file 'hooks/db-relation-departed'
--- hooks/db-relation-departed 1970-01-01 00:00:00 +0000
+++ hooks/db-relation-departed 2012-08-21 13:22:29 +0000
@@ -0,0 +1,5 @@
1#!/bin/bash
2
3source inc/common
4
5rm -f $config_file_path
06
=== modified file 'hooks/install'
--- hooks/install 2011-10-18 06:11:17 +0000
+++ hooks/install 2012-08-21 13:22:29 +0000
@@ -1,3 +1,58 @@
1#!/bin/bash1#!/bin/bash
22
3apt-get -y install wordpress pwgen3add-apt-repository ppa:charmers/charm-helpers
4
5apt-get update && apt-get -y upgrade
6apt-get -y install php5-memcache mysql-client pwgen nginx php5 php5-fpm php-apc mailutils php-mail sysstat php5-mysql php5-mcrypt charm-helper-sh s3cmd php5-curl rsync nfs-common bzr git-core mktemp
7
8modprobe nfs
9
10juju-log "Removing PHP Default Cron ..."
11rm -f /etc/cron.d/php5
12
13juju-log "Making /mnt/tmp dir ..."
14mkdir -p /mnt/tmp
15chmod 1777 /mnt/tmp
16
17juju-log "Making /mnt/logs ..."
18mkdir -p /mnt/logs/php-fpm
19chmod -R 1777 /mnt/logs
20
21## dont make this an actual ramdisk until later when size can be safely tested
22## just using plain disk cache for now
23juju-log "Making Ramdisk mount point and config ..."
24mkdir -p /mnt/ramdisk/proxy-cache
25mkdir -p /mnt/ramdisk/phpfpm-cache
26chmod -R 1777 /mnt/ramdisk
27
28juju-log "Cleaning any old or default nginx site configs ..."
29rm -f /etc/nginx/sites-enabled/*
30rm -f /etc/nginx/conf.d/*
31
32juju-log "Installing Nginx common config ..."
33rm -f /etc/nginx/nginx.conf
34install -o root -g root -m 0644 files/charm/nginx/etc_nginx_nginx.conf /etc/nginx/nginx.conf
35
36juju-log "Installing Nginx actual site config ..."
37#rm -f /etc/nginx/sites-available/
38install -o root -g root -m 0644 files/charm/nginx/etc_nginx_sites-enabled_wordpress /etc/nginx/sites-available/wordpress
39ln -sf ../sites-available/wordpress /etc/nginx/sites-enabled/wordpress
40
41juju-log "Installing Nginx loadbal config ..."
42rm -f /etc/nginx/sites-available/loadbalancer
43install -o root -g root -m 0644 files/charm/nginx/etc_nginx_sites-enabled_loadbalancer /etc/nginx/sites-available/loadbalancer
44ln -sf ../sites-available/loadbalancer /etc/nginx/sites-enabled/loadbalancer
45
46juju-log "Installing PHP-FPM pool configs ..."
47rm -f /etc/php5/fpm/pool.d/*
48install -o root -g root -m 0644 files/charm/php/php5-fpm_pool.d_www.conf /etc/php5/fpm/pool.d/www.conf
49
50juju-log "Moving PHP and Nginx var dirs to /mnt storage ..."
51rsync -avz /var/lib/nginx /mnt/ && rm -rf /var/lib/nginx && ln -s /mnt/nginx /var/lib/
52rsync -avz /var/lib/php5 /mnt/ && rm -rf /var/lib/php5 && ln -s /mnt/php5 /var/lib/
53
54juju-log "Restarting Services ..."
55service php5-fpm restart
56service nginx restart
57
58juju-log "So, environment is setup. We'll wait for some hooks to fire off before we get all crazy"
459
=== added symlink 'hooks/loadbalancer-relation-broken'
=== target is u'loadbalancer-relation-joined'
=== added symlink 'hooks/loadbalancer-relation-departed'
=== target is u'loadbalancer-relation-joined'
=== added file 'hooks/loadbalancer-relation-joined'
--- hooks/loadbalancer-relation-joined 1970-01-01 00:00:00 +0000
+++ hooks/loadbalancer-relation-joined 2012-08-21 13:22:29 +0000
@@ -0,0 +1,124 @@
1#!/usr/bin/env python
2#
3# reverseproxy-relation-changed - hook for when reverse proxy relation changes
4#
5# Copyright (C) 2011 Canonical Ltd.
6# Author: Clint Byrum <clint.byrum@canonical.com>
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21
22import sys
23import os
24import subprocess
25import json
26import tempfile
27import glob
28
29from socket import getaddrinfo
30
31remote_unit = os.environ.get("JUJU_REMOTE_UNIT")
32
33service_name, _ = remote_unit.split("/")
34
35# TODO: maybe load this from disk for easier customization
36t1 = """# Generated by juju
37proxy_cache_path /mnt/ramdisk/proxy-cache levels=1:2 keys_zone=proxycache:5m max_size=1000m;
38"""
39# servers will go here
40template = """
41server {
42 listen 80 default;
43 server_name _;
44
45 location / {
46 set $no_cache "";
47
48 if ($request_method !~ ^(GET|HEAD)$) {
49 set $no_cache "1";
50 }
51
52 if ($no_cache = "1") {
53 add_header Set-Cookie "_mcnc=1; Max-Age=30; Path=/";
54 add_header X-Microcachable "0";
55 }
56
57 if ($http_cookie ~* "_mcnc") {
58 set $no_cache "1";
59 }
60
61 proxy_no_cache $no_cache;
62 proxy_cache_bypass $no_cache;
63
64 proxy_redirect http://backend /;
65 proxy_pass http://backend;
66 proxy_cache proxycache;
67 proxy_cache_key $scheme$host$request_method$request_uri;
68 proxy_cache_valid 200 60s;
69 proxy_cache_use_stale updating;
70
71 proxy_set_header Host $host;
72 proxy_set_header X-Real-IP $remote_addr;
73 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
74 proxy_set_header X-UA-Compatible "IE=Edge,chrome=1";
75
76 proxy_max_temp_file_size 1M;
77 }
78}
79"""
80
81units = []
82p = subprocess.Popen("relation-list", stdout=subprocess.PIPE)
83for unit in p.stdout:
84 units.append(unit.strip())
85
86print units
87
88servers = """
89upstream backend {
90 server 127.0.0.1:8080;
91"""
92for unit in units:
93 p = subprocess.Popen(["relation-get", "private-address", unit],
94 stdout=subprocess.PIPE, close_fds=True)
95 paddress = p.stdout.read().strip()
96 p.wait()
97 # Add all configured units:
98 servers += (" server %s:8080;\n" % (paddress))
99servers += '}'
100
101print servers
102
103with tempfile.NamedTemporaryFile(dir="/etc/nginx/sites-available/",prefix="loadbalancer", delete=False) as conf:
104 conf.write(t1 + servers + template)
105 try:
106 os.unlink("/etc/nginx/sites-available/loadbalancer.old")
107 except:
108 pass
109 try:
110 os.rename("/etc/nginx/sites-available/loadbalancer","/etc/nginx/sites-available/loadbalancer.old")
111 except:
112 pass
113 try:
114 os.rename(conf.name, "/etc/nginx/sites-available/loadbalancer")
115 except:
116 os.unlink(conf.name)
117
118
119
120# Just in case haproxy wouldn't start because of empty/bad configs before, start it now
121subprocess.call(["service", "nginx", "start"])
122subprocess.check_call(["service", "nginx", "reload"])
123
124subprocess.check_call(["open-port", "80"])
0125
=== added symlink 'hooks/nfs-relation-broken'
=== target is u'nfs-relation-departed'
=== added file 'hooks/nfs-relation-changed'
--- hooks/nfs-relation-changed 1970-01-01 00:00:00 +0000
+++ hooks/nfs-relation-changed 2012-08-21 13:22:29 +0000
@@ -0,0 +1,28 @@
1#!/bin/bash
2
3set -eu
4
5juju-log "We've got a mount"
6source inc/common
7
8options=`relation-get options`
9mountpoint=`relation-get mountpoint`
10fstype=`relation-get fstype`
11host=`relation-get private-address`
12
13if [ -z "$fstype" ] ; then
14 juju-log "Going to wait for some real data"
15 exit 0
16fi
17
18# Write this out to our .nfs-mount file
19cat > .nfs-mount <<EOF
20#!/bin/bash
21MOUNT_TYPE=$fstype
22MOUNT_OPS=$options
23MOUNT_SERVER=$host
24MOUNT_PATH=$mountpoint
25
26EOF
27
28. hooks/config-changed
029
=== added file 'hooks/nfs-relation-departed'
--- hooks/nfs-relation-departed 1970-01-01 00:00:00 +0000
+++ hooks/nfs-relation-departed 2012-08-21 13:22:29 +0000
@@ -0,0 +1,20 @@
1#!/bin/bash
2
3set -eu
4
5juju-log "We've got a mount"
6source inc/common
7
8if [ ! -f .nfs-mount ]; then
9 juju-log "I guess we've already done this."
10 exit 0
11fi
12
13source .nfs-mount
14umount /mnt/wordpress
15
16rm -f .nfs-mount
17
18juju-log "Fairwell nfs mount, we hardly knew you"
19
20. hooks/config-changed
021
=== added file 'hooks/restart'
--- hooks/restart 1970-01-01 00:00:00 +0000
+++ hooks/restart 2012-08-21 13:22:29 +0000
@@ -0,0 +1,4 @@
1#!/bin/bash
2
3. hooks/stop
4. hooks/start
05
=== modified file 'hooks/start'
--- hooks/start 2011-02-07 22:23:02 +0000
+++ hooks/start 2012-08-21 13:22:29 +0000
@@ -1,1 +1,4 @@
1#!/bin/bash1#!/bin/bash
2
3service php5-fpm restart
4service nginx restart
25
=== modified file 'hooks/stop'
--- hooks/stop 2011-10-14 10:22:29 +0000
+++ hooks/stop 2012-08-21 13:22:29 +0000
@@ -1,3 +1,6 @@
1#!/bin/bash1#!/bin/bash
2juju-log "Stopping apache"
3/etc/init.d/apache2 stop
4\ No newline at end of file2\ No newline at end of file
3
4service php5-fpm stop
5service nginx stop
6
7
58
=== modified symlink 'hooks/upgrade-charm' (properties changed: -x to +x)
=== target was u'install'
--- hooks/upgrade-charm 1970-01-01 00:00:00 +0000
+++ hooks/upgrade-charm 2012-08-21 13:22:29 +0000
@@ -0,0 +1,11 @@
1#!/bin/bash
2
3# Run the install again!
4hooks/install
5
6wp_install_path="/var/www/"
7
8chown -R www-data.www-data $wp_install_path
9
10source hooks/config-changed
11source hooks/restart
012
=== added directory 'inc'
=== added file 'inc/common'
--- inc/common 1970-01-01 00:00:00 +0000
+++ inc/common 2012-08-21 13:22:29 +0000
@@ -0,0 +1,139 @@
1#!/bin/bash
2
3
4hostname=`unit-get public-address`
5private_name=`hostname -f`
6
7wp_install_path="/var/www/"
8config_file_path="$wp_install_path/wp-config.php"
9config_info_path="$wp_install_path/wp-info.php"
10config_override_path="$wp_install_path/wp-overrides.php"
11
12####
13## The only hook who should ever remove the NFS mount is the nfs-relation-(departed|broken) hook
14## Everyone else should just assume it's there or try to mount it. We don't want to loose people's
15## data, they get mad when that happens
16####
17
18make_bare()
19{
20 # Un-do APC
21 rm -f /etc/php5/conf.d/apc.ini
22 # IF there is an NFS mount, get everything out of it
23 if [ -f .nfs-mount ]; then # Looks like someone got fancy with NFS
24 if [ -L $wp_install_path/wp-content ]; then # Check if we actually have a symlink
25 rm -f $wp_install_path/wp-content
26 rsync -az /mnt/wordpress/wp-content $wp_install_path
27 fi
28 fi
29
30 echo "<?php" > $config_override_path
31 juju-log "We are now bare"
32}
33
34make_single()
35{
36 make_bare
37 juju-log "Installing PHP apc.ini ..."
38 rm -f /etc/php5/conf.d/apc.ini
39 install -o root -g root -m 0644 files/charm/php/php5_conf.d_apc.ini /etc/php5/conf.d/apc.ini
40 do_nfs
41 juju-log "We are now single"
42}
43
44make_optimized()
45{
46 make_single
47 cat > $config_override_path <<EOF
48<?php
49define('DISALLOW_FILE_MODS', TRUE);
50EOF
51 juju-log "We are now optimized"
52}
53
54do_nfs()
55{
56 if [ -f .nfs-mount ]; then
57 # This has all the NFS mount stuff in it
58 source .nfs-mount
59 mkdir -p /mnt/wordpress
60 if grep -qs '/mnt/wordpress' /proc/mounts; then
61 juju-log "We're already mounted."
62 else
63 mount -t $MOUNT_TYPE -o $MOUNT_OPS $MOUNT_SERVER:$MOUNT_PATH /mnt/wordpress
64 if [ $? -ne 0 ]; then
65 juju-log "Could not connect to file-server"
66 exit 1 # OH THE HUMANITY OF IT ALL
67 fi
68 if [ ! -d /mnt/wordpress/wp-content ]; then
69 rsync -az $wp_install_path/wp-content /mnt/wordpress/
70 fi
71
72 mv $wp_install_path/wp-content $wp_install_path/wp-content.bak.$(date +%Y%m%d-%H%M%S) && rm -rf $wp_install_path/wp-content
73 ln -s /mnt/wordpress/wp-content $wp_install_path
74 juju-log "Mounted NFS"
75 fi
76 else
77 juju-log "There is no nfs mount, not sure what to do, so we'll just bail"
78 fi
79}
80
81do_vcs()
82{
83 local NEW_REPO_URL="$1"
84
85 if [ -z "$NEW_REPO_URL" ]; then
86 rm -f .wp-repo
87 return 0
88 fi
89
90 if [ -f .wp-repo ]; then
91 source .wp-repo
92 else
93 REPO_URL=""
94 fi
95
96 if [ "$NEW_REPO_URL" != "$REPO_URL" ]; then
97 # We need to determine what kind of URL this is
98 if [ $(echo "$NEW_REPO_URL" | egrep -Ec "^(lp:\~|bzr:\/\/|bzr+ssh:\/\/)") -eq 1 ]; then
99 local repo_type="bzr"
100 local cmd="branch"
101 elif [ $(echo "$NEW_REPO_URL" | egrep -Ec "(^git@|^git:\/\/|\.git$)") -gt 0 ]; then
102 local repo_type="git"
103 local cmd="clone"
104 else
105 return 1
106 fi
107
108 repo_path=$(mktemp -d)
109 $repo_type $cmd $NEW_REPO_URL $repo_path
110 # Write all our new data to disk
111 cat > .wp-repo <<EOF
112#!/bin/bash
113REPO_TYPE=$repo_type
114REPO_URL=$NEW_REPO_URL
115REPO_PATH=$repo_path
116REPO_CMD=$cmd
117
118EOF
119 source .wp-repo # Just so the rest of this can run all willy nilly
120 fi
121
122 if [ ! -d "$REPO_PATH" ]; then
123 mkdir -p "$REPO_PATH"
124 $REPO_TYPE $REPO_CMD $REPO_URL $REPO_PATH
125 fi
126
127 local cwd=$(pwd)
128 cd $REPO_PATH
129 $REPO_TYPE pull
130 cd $cwd
131
132 # This will clean up anything deleted from wp-content specifically
133 rsync -az --delete $REPO_PATH/wp-content/ $wp_install_path/wp-content/
134 # This is just incase people have modifications outside of the wp-content directory
135 rsync -az $REPO_PATH/ $wp_install_path/
136
137 # This should be an --exclude flag in the RSYNC command, but I'm not quite sure how to make that work properly.
138 rm -rf $wp_install_path/.$REPO_TYPE
139}
0140
=== modified file 'metadata.yaml'
--- metadata.yaml 2012-05-22 22:35:00 +0000
+++ metadata.yaml 2012-08-21 13:22:29 +0000
@@ -1,16 +1,22 @@
1name: wordpress1name: wordpress
2<<<<<<< TREE
2summary: "WordPress is a full featured web blogging tool"3summary: "WordPress is a full featured web blogging tool"
3maintainer: Clint Byrum <clint@ubuntu.com>4maintainer: Clint Byrum <clint@ubuntu.com>
5=======
6summary: "WordPress is a full featured web blogging tool, this charm deploys it."
7maintainer: Marco Ceppi <marco@ceppi.net>
8>>>>>>> MERGE-SOURCE
4description: |9description: |
5 WordPress is a full featured web blogging tool:10 This will install and setup WordPress optimized to run in the cloud. This install, in particular, will
6 - Instant publishing (no rebuilding)11 place Ngnix and php-fpm configured to scale horizontally with Nginx's reverse proxy
7 - Comment pingback support with spam protection
8 - Non-crufty URLs
9 - Themable
10 - Plugin support
11requires:12requires:
12 db:13 db:
13 interface: mysql14 interface: mysql
15 nfs:
16 interface: mount
14provides:17provides:
15 website:18 website:
16 interface: http19 interface: http
20peers:
21 loadbalancer:
22 interface: reversenginx
1723
=== modified file 'revision'
--- revision 2012-06-18 20:28:16 +0000
+++ revision 2012-08-21 13:22:29 +0000
@@ -1,1 +1,5 @@
1<<<<<<< TREE
134234
3=======
478
5>>>>>>> MERGE-SOURCE

Subscribers

People subscribed via source and target branches

to all changes: