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
1=== added file 'README.md'
2--- README.md 1970-01-01 00:00:00 +0000
3+++ README.md 2012-08-21 13:22:29 +0000
4@@ -0,0 +1,152 @@
5+# Overview
6+
7+WordPress 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
8+a single installation.
9+
10+# Installation
11+
12+This charm is available in the Juju Charm Store, to deploy you'll need at a minimum: a cloud environment, a working Juju installation,
13+and a successful bootstrap. Please refer to the [Juju Getting Started](https://juju.ubuntu.com/docs/getting-started.html) documentation before continuing.
14+
15+Once bootstrapped, deploy the MySQL charm then this WordPress charm:
16+
17+ juju deploy mysql
18+ juju deploy wordpress
19+
20+Add a relation between the two of them
21+
22+ juju add-relation wordpress mysql
23+
24+Expose the WordPress installation
25+
26+ juju expose wordpress
27+
28+# Configuration
29+
30+This WordPress charm comes with several tuning levels designed to encompass the different styles in which this charm will be used.
31+
32+## Tuning
33+
34+A use case for each tuning style is outlined below:
35+
36+### Bare
37+
38+The Bare configuration option is meant for those who wish to run the stock WordPress setup with no caching, no manipulation of data,
39+and no additional scale out features enabled. This is ideal if you intend to install additional plugins to deal with coordinating
40+WordPress 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
41+unit is created, though everything else will be turned off (WordPress caching, APC OpCode caching, and NFS file sharing).
42+
43+To run this WordPress charm under a bare tuning level execute the following:
44+
45+ juju set wordpress tuning=bare
46+
47+### Single
48+
49+When running in Single mode, this charm will make every attempt to provide a solid base for your WordPress install. By running in single
50+the following will be enabled: Nginx microcache, APC OpCode caching, WordPress caching module, and the ability to sync files via NFS.
51+While 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
52+traffic in-flux. It's recommended for long running scaled out versions that optimized is used. The removal of the file share speeds up
53+the site and servers ensuring that the most efficient set up is provided.
54+
55+To run this WordPress charm under a single tuning level execute the following:
56+
57+ juju set wordpress tuning=single
58+
59+### Optimized
60+
61+If 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
62+recommended configuration. When you run WordPress under an Optimized tuning level, the ability to install, edit, and upgrade themes and plugins
63+is 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.
64+Everything else provided in Single level is available. In order to install or modify plugins with this setup you'll need to edit and commit
65+them to a forked version of the charm in the files/wordpress/ directory.
66+
67+To run this WordPress charm under an optimized tuning level execute the following:
68+
69+ juju set wordpress tuning=optimized
70+
71+## wp-content
72+
73+In order to allow for custom WordPress content within the Juju charm a separate configuration option exists for pointing to any Git or Bzr
74+repository. An example of a valid formed wp-content repository can be found on the [Juju Tools Github page](https://github.com/jujutools/wordpress-site).
75+To 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
76+respective names:
77+
78+ juju set wordpress wp-content=git@host:path/repo.git
79+
80+or
81+
82+ juju set wordpress wp-content=http://host/path/repo.git
83+
84+or
85+
86+ juju set wordpress wp-content=git://host/path/repo.git
87+
88+If 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:
89+
90+For LaunchPad hosted repostiories:
91+
92+ juju set wordpress wp-content=lp:~username/path/repo
93+
94+For other Bzr repositories:
95+
96+ juju set wordpress wp-content=bzr://host/path/repo
97+
98+or
99+
100+ juju set wordpress wp-content=bzr+ssh://host/path/repo
101+
102+Setting 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
103+on the system and will not be removed.
104+
105+## debug
106+
107+This 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
108+is 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
109+to 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
110+and may provide a way for an intruder to DDoS the machine.
111+
112+ juju set wordpress debug=yes
113+
114+to disable
115+
116+ juju set wordpress debug=no
117+
118+The default is to have debugging disabled.
119+
120+# Caveats
121+
122+## Single mode and the scale-out
123+
124+If you're in Single mode and you want to/need to scale out, but you've been upgrading, modifying, and installing plugins + themes like
125+a 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
126+this writing only the NFS charm will work, but as more shared-fs charms come out (gluster, ceph, etc) that provide a shared-fs/mount
127+interface those should all work as well. In this example we'll use NFS:
128+
129+ juju deploy nfs
130+ juju add-relation nfs wordpress:nfs
131+
132+By 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
133+recommended that you first deploy the nfs mount, _then_ scale WordPress out. Failure to do so may result in data loss. Once nfs is deployed,
134+running, and related you can scale out the WordPress unit using the following command:
135+
136+ juju add-unit wordpress
137+
138+In 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
139+of units to add, so to add three more units:
140+
141+ juju add-unit -n3 wordpress
142+
143+## I don't want to run three different machines for one WP install
144+
145+There 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
146+the 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
147+up and decide to remove this configuration option from the `environments.yaml` file. Prior to your first deployment you'll need to add the
148+following line to your Juju Environments file:
149+
150+ placement: local
151+
152+This 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
153+both your database and WordPress install. After you've bootstrap'd the environment, deploy the MySQL and WordPress charms like you normally
154+would. Instead of seeing three nodes you'll only see one, but both of your services will have been deployed. *FROM THIS POINT* you should
155+either remove or comment out the `placement` line in the environments file. This will prevent issues from occurring when you try to deploy
156+additional services or try to scale out existing services.
157
158=== added file 'config.yaml'
159--- config.yaml 1970-01-01 00:00:00 +0000
160+++ config.yaml 2012-08-21 13:22:29 +0000
161@@ -0,0 +1,26 @@
162+options:
163+ tuning:
164+ type: string
165+ default: "single"
166+ description: |
167+ This is the tuning level for the WordPress setup. There are three options: "bare", "single", and "optimized".
168+ "bare" will give you a nearly un-altered WordPress setup, as if you'd downloaded and set it up yourself.
169+ "single" will provide you with everything you need to run a singlular unit of WordPress. This doesn't take in to
170+ consideration that you'll be scaling at all. However, it will allow you to use WordPress free of any troubles and pesky
171+ limitations that typically happen during "optimized". While you _can_ scale out with this setting I encourage you read the README
172+ "optimized" will give you a hardened WordPress setup. Some of the features in the Admin panel will be locked down and theme edits/plugins
173+ can only be updated through he charm. This is the recommended setup for those who are in serious need of constant scaling.
174+ wp-content:
175+ type: string
176+ default: "https://github.com/jujutools/wordpress-site.git"
177+ description: |
178+ This is a full repository path to where the WordPress wp-contents can be found. At this time both Git and Bzr are
179+ supported. An example of what a wp-content repository should look like can be found at http://github.com/jujutools/wordpress-site.
180+ debug:
181+ type: string
182+ default: "no"
183+ description: |
184+ 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.
185+ info.php will display the phpinfo information for that server while the apc.php will provide APC cache stats (as well as additional administrative
186+ options for APC).
187+
188
189=== modified file 'copyright'
190--- copyright 2011-06-10 11:20:27 +0000
191+++ copyright 2012-08-21 13:22:29 +0000
192@@ -1,7 +1,7 @@
193 Format: http://dep.debian.net/deps/dep5/
194
195 Files: *
196-Copyright: Copyright 2011, Canonical Ltd., All Rights Reserved.
197+Copyright: Copyright 2012, Marco Ceppi, All Rights Reserved.
198 License: GPL-3
199 This program is free software: you can redistribute it and/or modify
200 it under the terms of the GNU General Public License as published by
201
202=== added directory 'files'
203=== added directory 'files/_debug'
204=== added file 'files/_debug/apc.php'
205--- files/_debug/apc.php 1970-01-01 00:00:00 +0000
206+++ files/_debug/apc.php 2012-08-21 13:22:29 +0000
207@@ -0,0 +1,1364 @@
208+<?php
209+/*
210+ +----------------------------------------------------------------------+
211+ | APC |
212+ +----------------------------------------------------------------------+
213+ | Copyright (c) 2006-2011 The PHP Group |
214+ +----------------------------------------------------------------------+
215+ | This source file is subject to version 3.01 of the PHP license, |
216+ | that is bundled with this package in the file LICENSE, and is |
217+ | available through the world-wide-web at the following url: |
218+ | http://www.php.net/license/3_01.txt |
219+ | If you did not receive a copy of the PHP license and are unable to |
220+ | obtain it through the world-wide-web, please send a note to |
221+ | license@php.net so we can mail you a copy immediately. |
222+ +----------------------------------------------------------------------+
223+ | Authors: Ralf Becker <beckerr@php.net> |
224+ | Rasmus Lerdorf <rasmus@php.net> |
225+ | Ilia Alshanetsky <ilia@prohost.org> |
226+ +----------------------------------------------------------------------+
227+
228+ All other licensing and usage conditions are those of the PHP Group.
229+
230+*/
231+
232+$VERSION='$Id$';
233+
234+////////// READ OPTIONAL CONFIGURATION FILE ////////////
235+if (file_exists("apc.conf.php")) include("apc.conf.php");
236+////////////////////////////////////////////////////////
237+
238+////////// BEGIN OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////
239+
240+defaults('USE_AUTHENTICATION',0); // Use (internal) authentication - best choice if
241+ // no other authentication is available
242+ // If set to 0:
243+ // There will be no further authentication. You
244+ // will have to handle this by yourself!
245+ // If set to 1:
246+ // You need to change ADMIN_PASSWORD to make
247+ // this work!
248+defaults('ADMIN_USERNAME',''); // Admin Username
249+defaults('ADMIN_PASSWORD',''); // Admin Password - CHANGE THIS TO ENABLE!!!
250+
251+// (beckerr) I'm using a clear text password here, because I've no good idea how to let
252+// users generate a md5 or crypt password in a easy way to fill it in above
253+
254+//defaults('DATE_FORMAT', "d.m.Y H:i:s"); // German
255+defaults('DATE_FORMAT', ' m/d/Y h:i:s A'); // US
256+
257+defaults('GRAPH_SIZE',150); // Image size
258+
259+//defaults('PROXY', 'tcp://127.0.0.1:8080');
260+
261+////////// END OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////////
262+
263+
264+// "define if not defined"
265+function defaults($d,$v) {
266+ if (!defined($d)) define($d,$v); // or just @define(...)
267+}
268+
269+// rewrite $PHP_SELF to block XSS attacks
270+//
271+$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],''), ENT_QUOTES, 'UTF-8') : '';
272+$time = time();
273+$host = php_uname('n');
274+if($host) { $host = '('.$host.')'; }
275+if (isset($_SERVER['SERVER_ADDR'])) {
276+ $host .= ' ('.$_SERVER['SERVER_ADDR'].')';
277+}
278+
279+// operation constants
280+define('OB_HOST_STATS',1);
281+define('OB_SYS_CACHE',2);
282+define('OB_USER_CACHE',3);
283+define('OB_SYS_CACHE_DIR',4);
284+define('OB_VERSION_CHECK',9);
285+
286+// check validity of input variables
287+$vardom=array(
288+ 'OB' => '/^\d+$/', // operational mode switch
289+ 'CC' => '/^[01]$/', // clear cache requested
290+ 'DU' => '/^.*$/', // Delete User Key
291+ 'SH' => '/^[a-z0-9]+$/', // shared object description
292+
293+ 'IMG' => '/^[123]$/', // image to generate
294+ 'LO' => '/^1$/', // login requested
295+
296+ 'COUNT' => '/^\d+$/', // number of line displayed in list
297+ 'SCOPE' => '/^[AD]$/', // list view scope
298+ 'SORT1' => '/^[AHSMCDTZ]$/', // first sort key
299+ 'SORT2' => '/^[DA]$/', // second sort key
300+ 'AGGR' => '/^\d+$/', // aggregation by dir level
301+ 'SEARCH' => '~^[a-zA-Z0-1/_.-]*$~' // aggregation by dir level
302+);
303+
304+// default cache mode
305+$cache_mode='opcode';
306+
307+// cache scope
308+$scope_list=array(
309+ 'A' => 'cache_list',
310+ 'D' => 'deleted_list'
311+);
312+
313+// handle POST and GET requests
314+if (empty($_REQUEST)) {
315+ if (!empty($_GET) && !empty($_POST)) {
316+ $_REQUEST = array_merge($_GET, $_POST);
317+ } else if (!empty($_GET)) {
318+ $_REQUEST = $_GET;
319+ } else if (!empty($_POST)) {
320+ $_REQUEST = $_POST;
321+ } else {
322+ $_REQUEST = array();
323+ }
324+}
325+
326+// check parameter syntax
327+foreach($vardom as $var => $dom) {
328+ if (!isset($_REQUEST[$var])) {
329+ $MYREQUEST[$var]=NULL;
330+ } else if (!is_array($_REQUEST[$var]) && preg_match($dom.'D',$_REQUEST[$var])) {
331+ $MYREQUEST[$var]=$_REQUEST[$var];
332+ } else {
333+ $MYREQUEST[$var]=$_REQUEST[$var]=NULL;
334+ }
335+}
336+
337+// check parameter sematics
338+if (empty($MYREQUEST['SCOPE'])) $MYREQUEST['SCOPE']="A";
339+if (empty($MYREQUEST['SORT1'])) $MYREQUEST['SORT1']="H";
340+if (empty($MYREQUEST['SORT2'])) $MYREQUEST['SORT2']="D";
341+if (empty($MYREQUEST['OB'])) $MYREQUEST['OB']=OB_HOST_STATS;
342+if (!isset($MYREQUEST['COUNT'])) $MYREQUEST['COUNT']=20;
343+if (!isset($scope_list[$MYREQUEST['SCOPE']])) $MYREQUEST['SCOPE']='A';
344+
345+$MY_SELF=
346+ "$PHP_SELF".
347+ "?SCOPE=".$MYREQUEST['SCOPE'].
348+ "&SORT1=".$MYREQUEST['SORT1'].
349+ "&SORT2=".$MYREQUEST['SORT2'].
350+ "&COUNT=".$MYREQUEST['COUNT'];
351+$MY_SELF_WO_SORT=
352+ "$PHP_SELF".
353+ "?SCOPE=".$MYREQUEST['SCOPE'].
354+ "&COUNT=".$MYREQUEST['COUNT'];
355+
356+// authentication needed?
357+//
358+if (!USE_AUTHENTICATION) {
359+ $AUTHENTICATED=1;
360+} else {
361+ $AUTHENTICATED=0;
362+ if (ADMIN_PASSWORD!='password' && ($MYREQUEST['LO'] == 1 || isset($_SERVER['PHP_AUTH_USER']))) {
363+
364+ if (!isset($_SERVER['PHP_AUTH_USER']) ||
365+ !isset($_SERVER['PHP_AUTH_PW']) ||
366+ $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||
367+ $_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {
368+ Header("WWW-Authenticate: Basic realm=\"APC Login\"");
369+ Header("HTTP/1.0 401 Unauthorized");
370+
371+ echo <<<EOB
372+ <html><body>
373+ <h1>Rejected!</h1>
374+ <big>Wrong Username or Password!</big><br/>&nbsp;<br/>&nbsp;
375+ <big><a href='$PHP_SELF?OB={$MYREQUEST['OB']}'>Continue...</a></big>
376+ </body></html>
377+EOB;
378+ exit;
379+
380+ } else {
381+ $AUTHENTICATED=1;
382+ }
383+ }
384+}
385+
386+// select cache mode
387+if ($AUTHENTICATED && $MYREQUEST['OB'] == OB_USER_CACHE) {
388+ $cache_mode='user';
389+}
390+// clear cache
391+if ($AUTHENTICATED && isset($MYREQUEST['CC']) && $MYREQUEST['CC']) {
392+ apc_clear_cache($cache_mode);
393+}
394+
395+if ($AUTHENTICATED && !empty($MYREQUEST['DU'])) {
396+ apc_delete($MYREQUEST['DU']);
397+}
398+
399+if(!function_exists('apc_cache_info') || !($cache=@apc_cache_info($cache_mode))) {
400+ echo "No cache info available. APC does not appear to be running.";
401+ exit;
402+}
403+
404+$cache_user = apc_cache_info('user', 1);
405+$mem=apc_sma_info();
406+if(!$cache['num_hits']) { $cache['num_hits']=1; $time++; } // Avoid division by 0 errors on a cache clear
407+
408+// don't cache this page
409+//
410+header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
411+header("Cache-Control: post-check=0, pre-check=0", false);
412+header("Pragma: no-cache"); // HTTP/1.0
413+
414+function duration($ts) {
415+ global $time;
416+ $years = (int)((($time - $ts)/(7*86400))/52.177457);
417+ $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400));
418+ $weeks = (int)(($rem)/(7*86400));
419+ $days = (int)(($rem)/86400) - $weeks*7;
420+ $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24;
421+ $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60;
422+ $str = '';
423+ if($years==1) $str .= "$years year, ";
424+ if($years>1) $str .= "$years years, ";
425+ if($weeks==1) $str .= "$weeks week, ";
426+ if($weeks>1) $str .= "$weeks weeks, ";
427+ if($days==1) $str .= "$days day,";
428+ if($days>1) $str .= "$days days,";
429+ if($hours == 1) $str .= " $hours hour and";
430+ if($hours>1) $str .= " $hours hours and";
431+ if($mins == 1) $str .= " 1 minute";
432+ else $str .= " $mins minutes";
433+ return $str;
434+}
435+
436+// create graphics
437+//
438+function graphics_avail() {
439+ return extension_loaded('gd');
440+}
441+if (isset($MYREQUEST['IMG']))
442+{
443+ if (!graphics_avail()) {
444+ exit(0);
445+ }
446+
447+ function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) {
448+ $r=$diameter/2;
449+ $w=deg2rad((360+$start+($end-$start)/2)%360);
450+
451+
452+ if (function_exists("imagefilledarc")) {
453+ // exists only if GD 2.0.1 is avaliable
454+ imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE);
455+ imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE);
456+ imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED);
457+ } else {
458+ imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2);
459+ imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
460+ imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
461+ imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
462+ imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
463+ imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2);
464+ }
465+ if ($text) {
466+ if ($placeindex>0) {
467+ imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
468+ imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
469+
470+ } else {
471+ imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
472+ }
473+ }
474+ }
475+
476+ function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$text,$placeindex=0) {
477+ $r=$diameter/2;
478+ $w=deg2rad((360+$start+($end-$start)/2)%360);
479+
480+ if ($placeindex>0) {
481+ imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
482+ imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
483+
484+ } else {
485+ imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
486+ }
487+ }
488+
489+ function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') {
490+ global $col_black;
491+ $x1=$x+$w-1;
492+ $y1=$y+$h-1;
493+
494+ imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black);
495+ if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2);
496+ else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2);
497+ imagerectangle($im, $x, $y1, $x1, $y, $color1);
498+ if ($text) {
499+ if ($placeindex>0) {
500+
501+ if ($placeindex<16)
502+ {
503+ $px=5;
504+ $py=$placeindex*12+6;
505+ imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2);
506+ imageline($im,$x,$y+$h/2,$px+90,$py,$color2);
507+ imagestring($im,2,$px,$py-6,$text,$color1);
508+
509+ } else {
510+ if ($placeindex<31) {
511+ $px=$x+40*2;
512+ $py=($placeindex-15)*12+6;
513+ } else {
514+ $px=$x+40*2+100*intval(($placeindex-15)/15);
515+ $py=($placeindex%15)*12+6;
516+ }
517+ imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2);
518+ imageline($im,$x+$w,$y+$h/2,$px,$py,$color2);
519+ imagestring($im,2,$px+2,$py-6,$text,$color1);
520+ }
521+ } else {
522+ imagestring($im,4,$x+5,$y1-16,$text,$color1);
523+ }
524+ }
525+ }
526+
527+
528+ $size = GRAPH_SIZE; // image size
529+ if ($MYREQUEST['IMG']==3)
530+ $image = imagecreate(2*$size+150, $size+10);
531+ else
532+ $image = imagecreate($size+50, $size+10);
533+
534+ $col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
535+ $col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
536+ $col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
537+ $col_black = imagecolorallocate($image, 0, 0, 0);
538+ imagecolortransparent($image,$col_white);
539+
540+ switch ($MYREQUEST['IMG']) {
541+
542+ case 1:
543+ $s=$mem['num_seg']*$mem['seg_size'];
544+ $a=$mem['avail_mem'];
545+ $x=$y=$size/2;
546+ $fuzz = 0.000001;
547+
548+ // This block of code creates the pie chart. It is a lot more complex than you
549+ // would expect because we try to visualize any memory fragmentation as well.
550+ $angle_from = 0;
551+ $string_placement=array();
552+ for($i=0; $i<$mem['num_seg']; $i++) {
553+ $ptr = 0;
554+ $free = $mem['block_lists'][$i];
555+ uasort($free, 'block_sort');
556+ foreach($free as $block) {
557+ if($block['offset']!=$ptr) { // Used block
558+ $angle_to = $angle_from+($block['offset']-$ptr)/$s;
559+ if(($angle_to+$fuzz)>1) $angle_to = 1;
560+ if( ($angle_to*360) - ($angle_from*360) >= 1) {
561+ fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red);
562+ if (($angle_to-$angle_from)>0.05) {
563+ array_push($string_placement, array($angle_from,$angle_to));
564+ }
565+ }
566+ $angle_from = $angle_to;
567+ }
568+ $angle_to = $angle_from+($block['size'])/$s;
569+ if(($angle_to+$fuzz)>1) $angle_to = 1;
570+ if( ($angle_to*360) - ($angle_from*360) >= 1) {
571+ fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_green);
572+ if (($angle_to-$angle_from)>0.05) {
573+ array_push($string_placement, array($angle_from,$angle_to));
574+ }
575+ }
576+ $angle_from = $angle_to;
577+ $ptr = $block['offset']+$block['size'];
578+ }
579+ if ($ptr < $mem['seg_size']) { // memory at the end
580+ $angle_to = $angle_from + ($mem['seg_size'] - $ptr)/$s;
581+ if(($angle_to+$fuzz)>1) $angle_to = 1;
582+ fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red);
583+ if (($angle_to-$angle_from)>0.05) {
584+ array_push($string_placement, array($angle_from,$angle_to));
585+ }
586+ }
587+ }
588+ foreach ($string_placement as $angle) {
589+ text_arc($image,$x,$y,$size,$angle[0]*360,$angle[1]*360,$col_black,bsize($s*($angle[1]-$angle[0])));
590+ }
591+ break;
592+
593+ case 2:
594+ $s=$cache['num_hits']+$cache['num_misses'];
595+ $a=$cache['num_hits'];
596+
597+ fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s));
598+ fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s));
599+ break;
600+
601+ case 3:
602+ $s=$mem['num_seg']*$mem['seg_size'];
603+ $a=$mem['avail_mem'];
604+ $x=130;
605+ $y=1;
606+ $j=1;
607+
608+ // This block of code creates the bar chart. It is a lot more complex than you
609+ // would expect because we try to visualize any memory fragmentation as well.
610+ for($i=0; $i<$mem['num_seg']; $i++) {
611+ $ptr = 0;
612+ $free = $mem['block_lists'][$i];
613+ uasort($free, 'block_sort');
614+ foreach($free as $block) {
615+ if($block['offset']!=$ptr) { // Used block
616+ $h=(GRAPH_SIZE-5)*($block['offset']-$ptr)/$s;
617+ if ($h>0) {
618+ $j++;
619+ if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($block['offset']-$ptr),$j);
620+ else fill_box($image,$x,$y,50,$h,$col_black,$col_red);
621+ }
622+ $y+=$h;
623+ }
624+ $h=(GRAPH_SIZE-5)*($block['size'])/$s;
625+ if ($h>0) {
626+ $j++;
627+ if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_green,bsize($block['size']),$j);
628+ else fill_box($image,$x,$y,50,$h,$col_black,$col_green);
629+ }
630+ $y+=$h;
631+ $ptr = $block['offset']+$block['size'];
632+ }
633+ if ($ptr < $mem['seg_size']) { // memory at the end
634+ $h = (GRAPH_SIZE-5) * ($mem['seg_size'] - $ptr) / $s;
635+ if ($h > 0) {
636+ fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($mem['seg_size']-$ptr),$j++);
637+ }
638+ }
639+ }
640+ break;
641+ case 4:
642+ $s=$cache['num_hits']+$cache['num_misses'];
643+ $a=$cache['num_hits'];
644+
645+ fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s));
646+ fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s));
647+ break;
648+
649+ }
650+ header("Content-type: image/png");
651+ imagepng($image);
652+ exit;
653+}
654+
655+// pretty printer for byte values
656+//
657+function bsize($s) {
658+ foreach (array('','K','M','G') as $i => $k) {
659+ if ($s < 1024) break;
660+ $s/=1024;
661+ }
662+ return sprintf("%5.1f %sBytes",$s,$k);
663+}
664+
665+// sortable table header in "scripts for this host" view
666+function sortheader($key,$name,$extra='') {
667+ global $MYREQUEST, $MY_SELF_WO_SORT;
668+
669+ if ($MYREQUEST['SORT1']==$key) {
670+ $MYREQUEST['SORT2'] = $MYREQUEST['SORT2']=='A' ? 'D' : 'A';
671+ }
672+ return "<a class=sortable href=\"$MY_SELF_WO_SORT$extra&SORT1=$key&SORT2=".$MYREQUEST['SORT2']."\">$name</a>";
673+
674+}
675+
676+// create menu entry
677+function menu_entry($ob,$title) {
678+ global $MYREQUEST,$MY_SELF;
679+ if ($MYREQUEST['OB']!=$ob) {
680+ return "<li><a href=\"$MY_SELF&OB=$ob\">$title</a></li>";
681+ } else if (empty($MYREQUEST['SH'])) {
682+ return "<li><span class=active>$title</span></li>";
683+ } else {
684+ return "<li><a class=\"child_active\" href=\"$MY_SELF&OB=$ob\">$title</a></li>";
685+ }
686+}
687+
688+function put_login_link($s="Login")
689+{
690+ global $MY_SELF,$MYREQUEST,$AUTHENTICATED;
691+ // needs ADMIN_PASSWORD to be changed!
692+ //
693+ if (!USE_AUTHENTICATION) {
694+ return;
695+ } else if (ADMIN_PASSWORD=='password')
696+ {
697+ print <<<EOB
698+ <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>
699+EOB;
700+ } else if ($AUTHENTICATED) {
701+ print <<<EOB
702+ '{$_SERVER['PHP_AUTH_USER']}'&nbsp;logged&nbsp;in!
703+EOB;
704+ } else{
705+ print <<<EOB
706+ <a href="$MY_SELF&LO=1&OB={$MYREQUEST['OB']}">$s</a>
707+EOB;
708+ }
709+}
710+
711+function block_sort($array1, $array2)
712+{
713+ if ($array1['offset'] > $array2['offset']) {
714+ return 1;
715+ } else {
716+ return -1;
717+ }
718+}
719+
720+
721+?>
722+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
723+<html>
724+<head><title>APC INFO <?php echo $host ?></title>
725+<style><!--
726+body { background:white; font-size:100.01%; margin:0; padding:0; }
727+body,p,td,th,input,submit { font-size:0.8em;font-family:arial,helvetica,sans-serif; }
728+* html body {font-size:0.8em}
729+* html p {font-size:0.8em}
730+* html td {font-size:0.8em}
731+* html th {font-size:0.8em}
732+* html input {font-size:0.8em}
733+* html submit {font-size:0.8em}
734+td { vertical-align:top }
735+a { color:black; font-weight:none; text-decoration:none; }
736+a:hover { text-decoration:underline; }
737+div.content { padding:1em 1em 1em 1em; position:absolute; width:97%; z-index:100; }
738+
739+
740+div.head div.login {
741+ position:absolute;
742+ right: 1em;
743+ top: 1.2em;
744+ color:white;
745+ width:6em;
746+ }
747+div.head div.login a {
748+ position:absolute;
749+ right: 0em;
750+ background:rgb(119,123,180);
751+ border:solid rgb(102,102,153) 2px;
752+ color:white;
753+ font-weight:bold;
754+ padding:0.1em 0.5em 0.1em 0.5em;
755+ text-decoration:none;
756+ }
757+div.head div.login a:hover {
758+ background:rgb(193,193,244);
759+ }
760+
761+h1.apc { background:rgb(153,153,204); margin:0; padding:0.5em 1em 0.5em 1em; }
762+* html h1.apc { margin-bottom:-7px; }
763+h1.apc a:hover { text-decoration:none; color:rgb(90,90,90); }
764+h1.apc div.logo span.logo {
765+ background:rgb(119,123,180);
766+ color:black;
767+ border-right: solid black 1px;
768+ border-bottom: solid black 1px;
769+ font-style:italic;
770+ font-size:1em;
771+ padding-left:1.2em;
772+ padding-right:1.2em;
773+ text-align:right;
774+ }
775+h1.apc div.logo span.name { color:white; font-size:0.7em; padding:0 0.8em 0 2em; }
776+h1.apc div.nameinfo { color:white; display:inline; font-size:0.4em; margin-left: 3em; }
777+h1.apc div.copy { color:black; font-size:0.4em; position:absolute; right:1em; }
778+hr.apc {
779+ background:white;
780+ border-bottom:solid rgb(102,102,153) 1px;
781+ border-style:none;
782+ border-top:solid rgb(102,102,153) 10px;
783+ height:12px;
784+ margin:0;
785+ margin-top:1px;
786+ padding:0;
787+}
788+
789+ol,menu { margin:1em 0 0 0; padding:0.2em; margin-left:1em;}
790+ol.menu li { display:inline; margin-right:0.7em; list-style:none; font-size:85%}
791+ol.menu a {
792+ background:rgb(153,153,204);
793+ border:solid rgb(102,102,153) 2px;
794+ color:white;
795+ font-weight:bold;
796+ margin-right:0em;
797+ padding:0.1em 0.5em 0.1em 0.5em;
798+ text-decoration:none;
799+ margin-left: 5px;
800+ }
801+ol.menu a.child_active {
802+ background:rgb(153,153,204);
803+ border:solid rgb(102,102,153) 2px;
804+ color:white;
805+ font-weight:bold;
806+ margin-right:0em;
807+ padding:0.1em 0.5em 0.1em 0.5em;
808+ text-decoration:none;
809+ border-left: solid black 5px;
810+ margin-left: 0px;
811+ }
812+ol.menu span.active {
813+ background:rgb(153,153,204);
814+ border:solid rgb(102,102,153) 2px;
815+ color:black;
816+ font-weight:bold;
817+ margin-right:0em;
818+ padding:0.1em 0.5em 0.1em 0.5em;
819+ text-decoration:none;
820+ border-left: solid black 5px;
821+ }
822+ol.menu span.inactive {
823+ background:rgb(193,193,244);
824+ border:solid rgb(182,182,233) 2px;
825+ color:white;
826+ font-weight:bold;
827+ margin-right:0em;
828+ padding:0.1em 0.5em 0.1em 0.5em;
829+ text-decoration:none;
830+ margin-left: 5px;
831+ }
832+ol.menu a:hover {
833+ background:rgb(193,193,244);
834+ text-decoration:none;
835+ }
836+
837+
838+div.info {
839+ background:rgb(204,204,204);
840+ border:solid rgb(204,204,204) 1px;
841+ margin-bottom:1em;
842+ }
843+div.info h2 {
844+ background:rgb(204,204,204);
845+ color:black;
846+ font-size:1em;
847+ margin:0;
848+ padding:0.1em 1em 0.1em 1em;
849+ }
850+div.info table {
851+ border:solid rgb(204,204,204) 1px;
852+ border-spacing:0;
853+ width:100%;
854+ }
855+div.info table th {
856+ background:rgb(204,204,204);
857+ color:white;
858+ margin:0;
859+ padding:0.1em 1em 0.1em 1em;
860+ }
861+div.info table th a.sortable { color:black; }
862+div.info table tr.tr-0 { background:rgb(238,238,238); }
863+div.info table tr.tr-1 { background:rgb(221,221,221); }
864+div.info table td { padding:0.3em 1em 0.3em 1em; }
865+div.info table td.td-0 { border-right:solid rgb(102,102,153) 1px; white-space:nowrap; }
866+div.info table td.td-n { border-right:solid rgb(102,102,153) 1px; }
867+div.info table td h3 {
868+ color:black;
869+ font-size:1.1em;
870+ margin-left:-0.3em;
871+ }
872+
873+div.graph { margin-bottom:1em }
874+div.graph h2 { background:rgb(204,204,204);; color:black; font-size:1em; margin:0; padding:0.1em 1em 0.1em 1em; }
875+div.graph table { border:solid rgb(204,204,204) 1px; color:black; font-weight:normal; width:100%; }
876+div.graph table td.td-0 { background:rgb(238,238,238); }
877+div.graph table td.td-1 { background:rgb(221,221,221); }
878+div.graph table td { padding:0.2em 1em 0.4em 1em; }
879+
880+div.div1,div.div2 { margin-bottom:1em; width:35em; }
881+div.div3 { position:absolute; left:40em; top:1em; width:580px; }
882+//div.div3 { position:absolute; left:37em; top:1em; right:1em; }
883+
884+div.sorting { margin:1.5em 0em 1.5em 2em }
885+.center { text-align:center }
886+.aright { position:absolute;right:1em }
887+.right { text-align:right }
888+.ok { color:rgb(0,200,0); font-weight:bold}
889+.failed { color:rgb(200,0,0); font-weight:bold}
890+
891+span.box {
892+ border: black solid 1px;
893+ border-right:solid black 2px;
894+ border-bottom:solid black 2px;
895+ padding:0 0.5em 0 0.5em;
896+ margin-right:1em;
897+}
898+span.green { background:#60F060; padding:0 0.5em 0 0.5em}
899+span.red { background:#D06030; padding:0 0.5em 0 0.5em }
900+
901+div.authneeded {
902+ background:rgb(238,238,238);
903+ border:solid rgb(204,204,204) 1px;
904+ color:rgb(200,0,0);
905+ font-size:1.2em;
906+ font-weight:bold;
907+ padding:2em;
908+ text-align:center;
909+ }
910+
911+input {
912+ background:rgb(153,153,204);
913+ border:solid rgb(102,102,153) 2px;
914+ color:white;
915+ font-weight:bold;
916+ margin-right:1em;
917+ padding:0.1em 0.5em 0.1em 0.5em;
918+ }
919+//-->
920+</style>
921+</head>
922+<body>
923+<div class="head">
924+ <h1 class="apc">
925+ <div class="logo"><span class="logo"><a href="http://pecl.php.net/package/APC">APC</a></span></div>
926+ <div class="nameinfo">Opcode Cache</div>
927+ </h1>
928+ <div class="login">
929+ <?php put_login_link(); ?>
930+ </div>
931+ <hr class="apc">
932+</div>
933+<?php
934+
935+
936+// Display main Menu
937+echo <<<EOB
938+ <ol class=menu>
939+ <li><a href="$MY_SELF&OB={$MYREQUEST['OB']}&SH={$MYREQUEST['SH']}">Refresh Data</a></li>
940+EOB;
941+echo
942+ menu_entry(1,'View Host Stats'),
943+ menu_entry(2,'System Cache Entries');
944+if ($AUTHENTICATED) {
945+ echo menu_entry(4,'Per-Directory Entries');
946+}
947+echo
948+ menu_entry(3,'User Cache Entries'),
949+ menu_entry(9,'Version Check');
950+
951+if ($AUTHENTICATED) {
952+ echo <<<EOB
953+ <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>
954+EOB;
955+}
956+echo <<<EOB
957+ </ol>
958+EOB;
959+
960+
961+// CONTENT
962+echo <<<EOB
963+ <div class=content>
964+EOB;
965+
966+// MAIN SWITCH STATEMENT
967+
968+switch ($MYREQUEST['OB']) {
969+
970+
971+
972+
973+
974+// -----------------------------------------------
975+// Host Stats
976+// -----------------------------------------------
977+case OB_HOST_STATS:
978+ $mem_size = $mem['num_seg']*$mem['seg_size'];
979+ $mem_avail= $mem['avail_mem'];
980+ $mem_used = $mem_size-$mem_avail;
981+ $seg_size = bsize($mem['seg_size']);
982+ $req_rate = sprintf("%.2f",($cache['num_hits']+$cache['num_misses'])/($time-$cache['start_time']));
983+ $hit_rate = sprintf("%.2f",($cache['num_hits'])/($time-$cache['start_time']));
984+ $miss_rate = sprintf("%.2f",($cache['num_misses'])/($time-$cache['start_time']));
985+ $insert_rate = sprintf("%.2f",($cache['num_inserts'])/($time-$cache['start_time']));
986+ $req_rate_user = sprintf("%.2f",($cache_user['num_hits']+$cache_user['num_misses'])/($time-$cache_user['start_time']));
987+ $hit_rate_user = sprintf("%.2f",($cache_user['num_hits'])/($time-$cache_user['start_time']));
988+ $miss_rate_user = sprintf("%.2f",($cache_user['num_misses'])/($time-$cache_user['start_time']));
989+ $insert_rate_user = sprintf("%.2f",($cache_user['num_inserts'])/($time-$cache_user['start_time']));
990+ $apcversion = phpversion('apc');
991+ $phpversion = phpversion();
992+ $number_files = $cache['num_entries'];
993+ $size_files = bsize($cache['mem_size']);
994+ $number_vars = $cache_user['num_entries'];
995+ $size_vars = bsize($cache_user['mem_size']);
996+ $i=0;
997+ echo <<< EOB
998+ <div class="info div1"><h2>General Cache Information</h2>
999+ <table cellspacing=0><tbody>
1000+ <tr class=tr-0><td class=td-0>APC Version</td><td>$apcversion</td></tr>
1001+ <tr class=tr-1><td class=td-0>PHP Version</td><td>$phpversion</td></tr>
1002+EOB;
1003+
1004+ if(!empty($_SERVER['SERVER_NAME']))
1005+ echo "<tr class=tr-0><td class=td-0>APC Host</td><td>{$_SERVER['SERVER_NAME']} $host</td></tr>\n";
1006+ if(!empty($_SERVER['SERVER_SOFTWARE']))
1007+ echo "<tr class=tr-1><td class=td-0>Server Software</td><td>{$_SERVER['SERVER_SOFTWARE']}</td></tr>\n";
1008+
1009+ echo <<<EOB
1010+ <tr class=tr-0><td class=td-0>Shared Memory</td><td>{$mem['num_seg']} Segment(s) with $seg_size
1011+ <br/> ({$cache['memory_type']} memory, {$cache['locking_type']} locking)
1012+ </td></tr>
1013+EOB;
1014+ echo '<tr class=tr-1><td class=td-0>Start Time</td><td>',date(DATE_FORMAT,$cache['start_time']),'</td></tr>';
1015+ echo '<tr class=tr-0><td class=td-0>Uptime</td><td>',duration($cache['start_time']),'</td></tr>';
1016+ echo '<tr class=tr-1><td class=td-0>File Upload Support</td><td>',$cache['file_upload_progress'],'</td></tr>';
1017+ echo <<<EOB
1018+ </tbody></table>
1019+ </div>
1020+
1021+ <div class="info div1"><h2>File Cache Information</h2>
1022+ <table cellspacing=0><tbody>
1023+ <tr class=tr-0><td class=td-0>Cached Files</td><td>$number_files ($size_files)</td></tr>
1024+ <tr class=tr-1><td class=td-0>Hits</td><td>{$cache['num_hits']}</td></tr>
1025+ <tr class=tr-0><td class=td-0>Misses</td><td>{$cache['num_misses']}</td></tr>
1026+ <tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate cache requests/second</td></tr>
1027+ <tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate cache requests/second</td></tr>
1028+ <tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate cache requests/second</td></tr>
1029+ <tr class=tr-0><td class=td-0>Insert Rate</td><td>$insert_rate cache requests/second</td></tr>
1030+ <tr class=tr-1><td class=td-0>Cache full count</td><td>{$cache['expunges']}</td></tr>
1031+ </tbody></table>
1032+ </div>
1033+
1034+ <div class="info div1"><h2>User Cache Information</h2>
1035+ <table cellspacing=0><tbody>
1036+ <tr class=tr-0><td class=td-0>Cached Variables</td><td>$number_vars ($size_vars)</td></tr>
1037+ <tr class=tr-1><td class=td-0>Hits</td><td>{$cache_user['num_hits']}</td></tr>
1038+ <tr class=tr-0><td class=td-0>Misses</td><td>{$cache_user['num_misses']}</td></tr>
1039+ <tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate_user cache requests/second</td></tr>
1040+ <tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate_user cache requests/second</td></tr>
1041+ <tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate_user cache requests/second</td></tr>
1042+ <tr class=tr-0><td class=td-0>Insert Rate</td><td>$insert_rate_user cache requests/second</td></tr>
1043+ <tr class=tr-1><td class=td-0>Cache full count</td><td>{$cache_user['expunges']}</td></tr>
1044+
1045+ </tbody></table>
1046+ </div>
1047+
1048+ <div class="info div2"><h2>Runtime Settings</h2><table cellspacing=0><tbody>
1049+EOB;
1050+
1051+ $j = 0;
1052+ foreach (ini_get_all('apc') as $k => $v) {
1053+ echo "<tr class=tr-$j><td class=td-0>",$k,"</td><td>",str_replace(',',',<br />',$v['local_value']),"</td></tr>\n";
1054+ $j = 1 - $j;
1055+ }
1056+
1057+ if($mem['num_seg']>1 || $mem['num_seg']==1 && count($mem['block_lists'][0])>1)
1058+ $mem_note = "Memory Usage<br /><font size=-2>(multiple slices indicate fragments)</font>";
1059+ else
1060+ $mem_note = "Memory Usage";
1061+
1062+ echo <<< EOB
1063+ </tbody></table>
1064+ </div>
1065+
1066+ <div class="graph div3"><h2>Host Status Diagrams</h2>
1067+ <table cellspacing=0><tbody>
1068+EOB;
1069+ $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10);
1070+ echo <<<EOB
1071+ <tr>
1072+ <td class=td-0>$mem_note</td>
1073+ <td class=td-1>Hits &amp; Misses</td>
1074+ </tr>
1075+EOB;
1076+
1077+ echo
1078+ graphics_avail() ?
1079+ '<tr>'.
1080+ "<td class=td-0><img alt=\"\" $size src=\"$PHP_SELF?IMG=1&$time\"></td>".
1081+ "<td class=td-1><img alt=\"\" $size src=\"$PHP_SELF?IMG=2&$time\"></td></tr>\n"
1082+ : "",
1083+ '<tr>',
1084+ '<td class=td-0><span class="green box">&nbsp;</span>Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size),"</td>\n",
1085+ '<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",
1086+ '</tr>',
1087+ '<tr>',
1088+ '<td class=td-0><span class="red box">&nbsp;</span>Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size),"</td>\n",
1089+ '<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";
1090+ echo <<< EOB
1091+ </tr>
1092+ </tbody></table>
1093+
1094+ <br/>
1095+ <h2>Detailed Memory Usage and Fragmentation</h2>
1096+ <table cellspacing=0><tbody>
1097+ <tr>
1098+ <td class=td-0 colspan=2><br/>
1099+EOB;
1100+
1101+ // Fragementation: (freeseg - 1) / total_seg
1102+ $nseg = $freeseg = $fragsize = $freetotal = 0;
1103+ for($i=0; $i<$mem['num_seg']; $i++) {
1104+ $ptr = 0;
1105+ foreach($mem['block_lists'][$i] as $block) {
1106+ if ($block['offset'] != $ptr) {
1107+ ++$nseg;
1108+ }
1109+ $ptr = $block['offset'] + $block['size'];
1110+ /* Only consider blocks <5M for the fragmentation % */
1111+ if($block['size']<(5*1024*1024)) $fragsize+=$block['size'];
1112+ $freetotal+=$block['size'];
1113+ }
1114+ $freeseg += count($mem['block_lists'][$i]);
1115+ }
1116+
1117+ if ($freeseg > 1) {
1118+ $frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize/$freetotal)*100,bsize($fragsize),bsize($freetotal),$freeseg);
1119+ } else {
1120+ $frag = "0%";
1121+ }
1122+
1123+ if (graphics_avail()) {
1124+ $size='width='.(2*GRAPH_SIZE+150).' height='.(GRAPH_SIZE+10);
1125+ echo <<<EOB
1126+ <img alt="" $size src="$PHP_SELF?IMG=3&$time">
1127+EOB;
1128+ }
1129+ echo <<<EOB
1130+ </br>Fragmentation: $frag
1131+ </td>
1132+ </tr>
1133+EOB;
1134+ if(isset($mem['adist'])) {
1135+ foreach($mem['adist'] as $i=>$v) {
1136+ $cur = pow(2,$i); $nxt = pow(2,$i+1)-1;
1137+ if($i==0) $range = "1";
1138+ else $range = "$cur - $nxt";
1139+ echo "<tr><th align=right>$range</th><td align=right>$v</td></tr>\n";
1140+ }
1141+ }
1142+ echo <<<EOB
1143+ </tbody></table>
1144+ </div>
1145+EOB;
1146+
1147+ break;
1148+
1149+
1150+// -----------------------------------------------
1151+// User Cache Entries
1152+// -----------------------------------------------
1153+case OB_USER_CACHE:
1154+ if (!$AUTHENTICATED) {
1155+ echo '<div class="error">You need to login to see the user values here!<br/>&nbsp;<br/>';
1156+ put_login_link("Login now!");
1157+ echo '</div>';
1158+ break;
1159+ }
1160+ $fieldname='info';
1161+ $fieldheading='User Entry Label';
1162+ $fieldkey='info';
1163+
1164+// -----------------------------------------------
1165+// System Cache Entries
1166+// -----------------------------------------------
1167+case OB_SYS_CACHE:
1168+ if (!isset($fieldname))
1169+ {
1170+ $fieldname='filename';
1171+ $fieldheading='Script Filename';
1172+ if(ini_get("apc.stat")) $fieldkey='inode';
1173+ else $fieldkey='filename';
1174+ }
1175+ if (!empty($MYREQUEST['SH']))
1176+ {
1177+ echo <<< EOB
1178+ <div class="info"><table cellspacing=0><tbody>
1179+ <tr><th>Attribute</th><th>Value</th></tr>
1180+EOB;
1181+
1182+ $m=0;
1183+ foreach($scope_list as $j => $list) {
1184+ foreach($cache[$list] as $i => $entry) {
1185+ if (md5($entry[$fieldkey])!=$MYREQUEST['SH']) continue;
1186+ foreach($entry as $k => $value) {
1187+ if (!$AUTHENTICATED) {
1188+ // hide all path entries if not logged in
1189+ $value=preg_replace('/^.*(\\/|\\\\)/','<i>&lt;hidden&gt;</i>/',$value);
1190+ }
1191+
1192+ if ($k == "num_hits") {
1193+ $value=sprintf("%s (%.2f%%)",$value,$value*100/$cache['num_hits']);
1194+ }
1195+ if ($k == 'deletion_time') {
1196+ if(!$entry['deletion_time']) $value = "None";
1197+ }
1198+ echo
1199+ "<tr class=tr-$m>",
1200+ "<td class=td-0>",ucwords(preg_replace("/_/"," ",$k)),"</td>",
1201+ "<td class=td-last>",(preg_match("/time/",$k) && $value!='None') ? date(DATE_FORMAT,$value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),"</td>",
1202+ "</tr>";
1203+ $m=1-$m;
1204+ }
1205+ if($fieldkey=='info') {
1206+ echo "<tr class=tr-$m><td class=td-0>Stored Value</td><td class=td-last><pre>";
1207+ $output = var_export(apc_fetch($entry[$fieldkey]),true);
1208+ echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
1209+ echo "</pre></td></tr>\n";
1210+ }
1211+ break;
1212+ }
1213+ }
1214+
1215+ echo <<<EOB
1216+ </tbody></table>
1217+ </div>
1218+EOB;
1219+ break;
1220+ }
1221+
1222+ $cols=6;
1223+ echo <<<EOB
1224+ <div class=sorting><form>Scope:
1225+ <input type=hidden name=OB value={$MYREQUEST['OB']}>
1226+ <select name=SCOPE>
1227+EOB;
1228+ echo
1229+ "<option value=A",$MYREQUEST['SCOPE']=='A' ? " selected":"",">Active</option>",
1230+ "<option value=D",$MYREQUEST['SCOPE']=='D' ? " selected":"",">Deleted</option>",
1231+ "</select>",
1232+ ", Sorting:<select name=SORT1>",
1233+ "<option value=H",$MYREQUEST['SORT1']=='H' ? " selected":"",">Hits</option>",
1234+ "<option value=Z",$MYREQUEST['SORT1']=='Z' ? " selected":"",">Size</option>",
1235+ "<option value=S",$MYREQUEST['SORT1']=='S' ? " selected":"",">$fieldheading</option>",
1236+ "<option value=A",$MYREQUEST['SORT1']=='A' ? " selected":"",">Last accessed</option>",
1237+ "<option value=M",$MYREQUEST['SORT1']=='M' ? " selected":"",">Last modified</option>",
1238+ "<option value=C",$MYREQUEST['SORT1']=='C' ? " selected":"",">Created at</option>",
1239+ "<option value=D",$MYREQUEST['SORT1']=='D' ? " selected":"",">Deleted at</option>";
1240+ if($fieldname=='info') echo
1241+ "<option value=D",$MYREQUEST['SORT1']=='T' ? " selected":"",">Timeout</option>";
1242+ echo
1243+ '</select>',
1244+ '<select name=SORT2>',
1245+ '<option value=D',$MYREQUEST['SORT2']=='D' ? ' selected':'','>DESC</option>',
1246+ '<option value=A',$MYREQUEST['SORT2']=='A' ? ' selected':'','>ASC</option>',
1247+ '</select>',
1248+ '<select name=COUNT onChange="form.submit()">',
1249+ '<option value=10 ',$MYREQUEST['COUNT']=='10' ? ' selected':'','>Top 10</option>',
1250+ '<option value=20 ',$MYREQUEST['COUNT']=='20' ? ' selected':'','>Top 20</option>',
1251+ '<option value=50 ',$MYREQUEST['COUNT']=='50' ? ' selected':'','>Top 50</option>',
1252+ '<option value=100',$MYREQUEST['COUNT']=='100'? ' selected':'','>Top 100</option>',
1253+ '<option value=150',$MYREQUEST['COUNT']=='150'? ' selected':'','>Top 150</option>',
1254+ '<option value=200',$MYREQUEST['COUNT']=='200'? ' selected':'','>Top 200</option>',
1255+ '<option value=500',$MYREQUEST['COUNT']=='500'? ' selected':'','>Top 500</option>',
1256+ '<option value=0 ',$MYREQUEST['COUNT']=='0' ? ' selected':'','>All</option>',
1257+ '</select>',
1258+ '&nbsp; Search: <input name=SEARCH value="',$MYREQUEST['SEARCH'],'" type=text size=25/>',
1259+ '&nbsp;<input type=submit value="GO!">',
1260+ '</form></div>';
1261+
1262+ if (isset($MYREQUEST['SEARCH'])) {
1263+ // Don't use preg_quote because we want the user to be able to specify a
1264+ // regular expression subpattern.
1265+ $MYREQUEST['SEARCH'] = '/'.str_replace('/', '\\/', $MYREQUEST['SEARCH']).'/i';
1266+ if (preg_match($MYREQUEST['SEARCH'], 'test') === false) {
1267+ echo '<div class="error">Error: enter a valid regular expression as a search query.</div>';
1268+ break;
1269+ }
1270+ }
1271+
1272+ echo
1273+ '<div class="info"><table cellspacing=0><tbody>',
1274+ '<tr>',
1275+ '<th>',sortheader('S',$fieldheading, "&OB=".$MYREQUEST['OB']),'</th>',
1276+ '<th>',sortheader('H','Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1277+ '<th>',sortheader('Z','Size', "&OB=".$MYREQUEST['OB']),'</th>',
1278+ '<th>',sortheader('A','Last accessed',"&OB=".$MYREQUEST['OB']),'</th>',
1279+ '<th>',sortheader('M','Last modified',"&OB=".$MYREQUEST['OB']),'</th>',
1280+ '<th>',sortheader('C','Created at', "&OB=".$MYREQUEST['OB']),'</th>';
1281+
1282+ if($fieldname=='info') {
1283+ $cols+=2;
1284+ echo '<th>',sortheader('T','Timeout',"&OB=".$MYREQUEST['OB']),'</th>';
1285+ }
1286+ echo '<th>',sortheader('D','Deleted at',"&OB=".$MYREQUEST['OB']),'</th></tr>';
1287+
1288+ // builds list with alpha numeric sortable keys
1289+ //
1290+ $list = array();
1291+ foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $i => $entry) {
1292+ switch($MYREQUEST['SORT1']) {
1293+ case 'A': $k=sprintf('%015d-',$entry['access_time']); break;
1294+ case 'H': $k=sprintf('%015d-',$entry['num_hits']); break;
1295+ case 'Z': $k=sprintf('%015d-',$entry['mem_size']); break;
1296+ case 'M': $k=sprintf('%015d-',$entry['mtime']); break;
1297+ case 'C': $k=sprintf('%015d-',$entry['creation_time']); break;
1298+ case 'T': $k=sprintf('%015d-',$entry['ttl']); break;
1299+ case 'D': $k=sprintf('%015d-',$entry['deletion_time']); break;
1300+ case 'S': $k=''; break;
1301+ }
1302+ if (!$AUTHENTICATED) {
1303+ // hide all path entries if not logged in
1304+ $list[$k.$entry[$fieldname]]=preg_replace('/^.*(\\/|\\\\)/','*hidden*/',$entry);
1305+ } else {
1306+ $list[$k.$entry[$fieldname]]=$entry;
1307+ }
1308+ }
1309+
1310+ if ($list) {
1311+
1312+ // sort list
1313+ //
1314+ switch ($MYREQUEST['SORT2']) {
1315+ case "A": krsort($list); break;
1316+ case "D": ksort($list); break;
1317+ }
1318+
1319+ // output list
1320+ $i=0;
1321+ foreach($list as $k => $entry) {
1322+ if(!$MYREQUEST['SEARCH'] || preg_match($MYREQUEST['SEARCH'], $entry[$fieldname]) != 0) {
1323+ $field_value = htmlentities(strip_tags($entry[$fieldname],''), ENT_QUOTES, 'UTF-8');
1324+ echo
1325+ '<tr class=tr-',$i%2,'>',
1326+ "<td class=td-0><a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&SH=",md5($entry[$fieldkey]),"\">",$field_value,'</a></td>',
1327+ '<td class="td-n center">',$entry['num_hits'],'</td>',
1328+ '<td class="td-n right">',$entry['mem_size'],'</td>',
1329+ '<td class="td-n center">',date(DATE_FORMAT,$entry['access_time']),'</td>',
1330+ '<td class="td-n center">',date(DATE_FORMAT,$entry['mtime']),'</td>',
1331+ '<td class="td-n center">',date(DATE_FORMAT,$entry['creation_time']),'</td>';
1332+
1333+ if($fieldname=='info') {
1334+ if($entry['ttl'])
1335+ echo '<td class="td-n center">'.$entry['ttl'].' seconds</td>';
1336+ else
1337+ echo '<td class="td-n center">None</td>';
1338+ }
1339+ if ($entry['deletion_time']) {
1340+
1341+ echo '<td class="td-last center">', date(DATE_FORMAT,$entry['deletion_time']), '</td>';
1342+ } else if ($MYREQUEST['OB'] == OB_USER_CACHE) {
1343+
1344+ echo '<td class="td-last center">';
1345+ echo '[<a href="', $MY_SELF, '&OB=', $MYREQUEST['OB'], '&DU=', urlencode($entry[$fieldkey]), '">Delete Now</a>]';
1346+ echo '</td>';
1347+ } else {
1348+ echo '<td class="td-last center"> &nbsp; </td>';
1349+ }
1350+ echo '</tr>';
1351+ $i++;
1352+ if ($i == $MYREQUEST['COUNT'])
1353+ break;
1354+ }
1355+ }
1356+
1357+ } else {
1358+ echo '<tr class=tr-0><td class="center" colspan=',$cols,'><i>No data</i></td></tr>';
1359+ }
1360+ echo <<< EOB
1361+ </tbody></table>
1362+EOB;
1363+
1364+ if ($list && $i < count($list)) {
1365+ echo "<a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&COUNT=0\"><i>",count($list)-$i,' more available...</i></a>';
1366+ }
1367+
1368+ echo <<< EOB
1369+ </div>
1370+EOB;
1371+ break;
1372+
1373+
1374+// -----------------------------------------------
1375+// Per-Directory System Cache Entries
1376+// -----------------------------------------------
1377+case OB_SYS_CACHE_DIR:
1378+ if (!$AUTHENTICATED) {
1379+ break;
1380+ }
1381+
1382+ echo <<<EOB
1383+ <div class=sorting><form>Scope:
1384+ <input type=hidden name=OB value={$MYREQUEST['OB']}>
1385+ <select name=SCOPE>
1386+EOB;
1387+ echo
1388+ "<option value=A",$MYREQUEST['SCOPE']=='A' ? " selected":"",">Active</option>",
1389+ "<option value=D",$MYREQUEST['SCOPE']=='D' ? " selected":"",">Deleted</option>",
1390+ "</select>",
1391+ ", Sorting:<select name=SORT1>",
1392+ "<option value=H",$MYREQUEST['SORT1']=='H' ? " selected":"",">Total Hits</option>",
1393+ "<option value=Z",$MYREQUEST['SORT1']=='Z' ? " selected":"",">Total Size</option>",
1394+ "<option value=T",$MYREQUEST['SORT1']=='T' ? " selected":"",">Number of Files</option>",
1395+ "<option value=S",$MYREQUEST['SORT1']=='S' ? " selected":"",">Directory Name</option>",
1396+ "<option value=A",$MYREQUEST['SORT1']=='A' ? " selected":"",">Avg. Size</option>",
1397+ "<option value=C",$MYREQUEST['SORT1']=='C' ? " selected":"",">Avg. Hits</option>",
1398+ '</select>',
1399+ '<select name=SORT2>',
1400+ '<option value=D',$MYREQUEST['SORT2']=='D' ? ' selected':'','>DESC</option>',
1401+ '<option value=A',$MYREQUEST['SORT2']=='A' ? ' selected':'','>ASC</option>',
1402+ '</select>',
1403+ '<select name=COUNT onChange="form.submit()">',
1404+ '<option value=10 ',$MYREQUEST['COUNT']=='10' ? ' selected':'','>Top 10</option>',
1405+ '<option value=20 ',$MYREQUEST['COUNT']=='20' ? ' selected':'','>Top 20</option>',
1406+ '<option value=50 ',$MYREQUEST['COUNT']=='50' ? ' selected':'','>Top 50</option>',
1407+ '<option value=100',$MYREQUEST['COUNT']=='100'? ' selected':'','>Top 100</option>',
1408+ '<option value=150',$MYREQUEST['COUNT']=='150'? ' selected':'','>Top 150</option>',
1409+ '<option value=200',$MYREQUEST['COUNT']=='200'? ' selected':'','>Top 200</option>',
1410+ '<option value=500',$MYREQUEST['COUNT']=='500'? ' selected':'','>Top 500</option>',
1411+ '<option value=0 ',$MYREQUEST['COUNT']=='0' ? ' selected':'','>All</option>',
1412+ '</select>',
1413+ ", Group By Dir Level:<select name=AGGR>",
1414+ "<option value='' selected>None</option>";
1415+ for ($i = 1; $i < 10; $i++)
1416+ echo "<option value=$i",$MYREQUEST['AGGR']==$i ? " selected":"",">$i</option>";
1417+ echo '</select>',
1418+ '&nbsp;<input type=submit value="GO!">',
1419+ '</form></div>',
1420+
1421+ '<div class="info"><table cellspacing=0><tbody>',
1422+ '<tr>',
1423+ '<th>',sortheader('S','Directory Name', "&OB=".$MYREQUEST['OB']),'</th>',
1424+ '<th>',sortheader('T','Number of Files',"&OB=".$MYREQUEST['OB']),'</th>',
1425+ '<th>',sortheader('H','Total Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1426+ '<th>',sortheader('Z','Total Size', "&OB=".$MYREQUEST['OB']),'</th>',
1427+ '<th>',sortheader('C','Avg. Hits', "&OB=".$MYREQUEST['OB']),'</th>',
1428+ '<th>',sortheader('A','Avg. Size', "&OB=".$MYREQUEST['OB']),'</th>',
1429+ '</tr>';
1430+
1431+ // builds list with alpha numeric sortable keys
1432+ //
1433+ $tmp = $list = array();
1434+ foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $entry) {
1435+ $n = dirname($entry['filename']);
1436+ if ($MYREQUEST['AGGR'] > 0) {
1437+ $n = preg_replace("!^(/?(?:[^/\\\\]+[/\\\\]){".($MYREQUEST['AGGR']-1)."}[^/\\\\]*).*!", "$1", $n);
1438+ }
1439+ if (!isset($tmp[$n])) {
1440+ $tmp[$n] = array('hits'=>0,'size'=>0,'ents'=>0);
1441+ }
1442+ $tmp[$n]['hits'] += $entry['num_hits'];
1443+ $tmp[$n]['size'] += $entry['mem_size'];
1444+ ++$tmp[$n]['ents'];
1445+ }
1446+
1447+ foreach ($tmp as $k => $v) {
1448+ switch($MYREQUEST['SORT1']) {
1449+ case 'A': $kn=sprintf('%015d-',$v['size'] / $v['ents']);break;
1450+ case 'T': $kn=sprintf('%015d-',$v['ents']); break;
1451+ case 'H': $kn=sprintf('%015d-',$v['hits']); break;
1452+ case 'Z': $kn=sprintf('%015d-',$v['size']); break;
1453+ case 'C': $kn=sprintf('%015d-',$v['hits'] / $v['ents']);break;
1454+ case 'S': $kn = $k; break;
1455+ }
1456+ $list[$kn.$k] = array($k, $v['ents'], $v['hits'], $v['size']);
1457+ }
1458+
1459+ if ($list) {
1460+
1461+ // sort list
1462+ //
1463+ switch ($MYREQUEST['SORT2']) {
1464+ case "A": krsort($list); break;
1465+ case "D": ksort($list); break;
1466+ }
1467+
1468+ // output list
1469+ $i = 0;
1470+ foreach($list as $entry) {
1471+ echo
1472+ '<tr class=tr-',$i%2,'>',
1473+ "<td class=td-0>",$entry[0],'</a></td>',
1474+ '<td class="td-n center">',$entry[1],'</td>',
1475+ '<td class="td-n center">',$entry[2],'</td>',
1476+ '<td class="td-n center">',$entry[3],'</td>',
1477+ '<td class="td-n center">',round($entry[2] / $entry[1]),'</td>',
1478+ '<td class="td-n center">',round($entry[3] / $entry[1]),'</td>',
1479+ '</tr>';
1480+
1481+ if (++$i == $MYREQUEST['COUNT']) break;
1482+ }
1483+
1484+ } else {
1485+ echo '<tr class=tr-0><td class="center" colspan=6><i>No data</i></td></tr>';
1486+ }
1487+ echo <<< EOB
1488+ </tbody></table>
1489+EOB;
1490+
1491+ if ($list && $i < count($list)) {
1492+ echo "<a href=\"$MY_SELF&OB=",$MYREQUEST['OB'],"&COUNT=0\"><i>",count($list)-$i,' more available...</i></a>';
1493+ }
1494+
1495+ echo <<< EOB
1496+ </div>
1497+EOB;
1498+ break;
1499+
1500+// -----------------------------------------------
1501+// Version check
1502+// -----------------------------------------------
1503+case OB_VERSION_CHECK:
1504+ echo <<<EOB
1505+ <div class="info"><h2>APC Version Information</h2>
1506+ <table cellspacing=0><tbody>
1507+ <tr>
1508+ <th></th>
1509+ </tr>
1510+EOB;
1511+ if (defined('PROXY')) {
1512+ $ctxt = stream_context_create( array( 'http' => array( 'proxy' => PROXY, 'request_fulluri' => True ) ) );
1513+ $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss", False, $ctxt);
1514+ } else {
1515+ $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss");
1516+ }
1517+ if (!$rss) {
1518+ echo '<tr class="td-last center"><td>Unable to fetch version information.</td></tr>';
1519+ } else {
1520+ $apcversion = phpversion('apc');
1521+
1522+ preg_match('!<title>APC ([0-9.]+)</title>!', $rss, $match);
1523+ echo '<tr class="tr-0 center"><td>';
1524+ if (version_compare($apcversion, $match[1], '>=')) {
1525+ echo '<div class="ok">You are running the latest version of APC ('.$apcversion.')</div>';
1526+ $i = 3;
1527+ } else {
1528+ echo '<div class="failed">You are running an older version of APC ('.$apcversion.'),
1529+ newer version '.$match[1].' is available at <a href="http://pecl.php.net/package/APC/'.$match[1].'">
1530+ http://pecl.php.net/package/APC/'.$match[1].'</a>
1531+ </div>';
1532+ $i = -1;
1533+ }
1534+ echo '</td></tr>';
1535+ echo '<tr class="tr-0"><td><h3>Change Log:</h3><br/>';
1536+
1537+ preg_match_all('!<(title|description)>([^<]+)</\\1>!', $rss, $match);
1538+ next($match[2]); next($match[2]);
1539+
1540+ while (list(,$v) = each($match[2])) {
1541+ list(,$ver) = explode(' ', $v, 2);
1542+ if ($i < 0 && version_compare($apcversion, $ver, '>=')) {
1543+ break;
1544+ } else if (!$i--) {
1545+ break;
1546+ }
1547+ echo "<b><a href=\"http://pecl.php.net/package/APC/$ver\">".htmlspecialchars($v, ENT_QUOTES, 'UTF-8')."</a></b><br><blockquote>";
1548+ echo nl2br(htmlspecialchars(current($match[2]), ENT_QUOTES, 'UTF-8'))."</blockquote>";
1549+ next($match[2]);
1550+ }
1551+ echo '</td></tr>';
1552+ }
1553+ echo <<< EOB
1554+ </tbody></table>
1555+ </div>
1556+EOB;
1557+ break;
1558+
1559+}
1560+
1561+echo <<< EOB
1562+ </div>
1563+EOB;
1564+
1565+?>
1566+
1567+<!-- <?php echo "\nBased on APCGUI By R.Becker\n$VERSION\n"?> -->
1568+</body>
1569+</html>
1570+
1571+
1572
1573=== added file 'files/_debug/info.php'
1574--- files/_debug/info.php 1970-01-01 00:00:00 +0000
1575+++ files/_debug/info.php 2012-08-21 13:22:29 +0000
1576@@ -0,0 +1,3 @@
1577+<?php
1578+
1579+phpinfo();
1580
1581=== added directory 'files/charm'
1582=== added directory 'files/charm/nginx'
1583=== added file 'files/charm/nginx/etc_nginx_nginx.conf'
1584--- files/charm/nginx/etc_nginx_nginx.conf 1970-01-01 00:00:00 +0000
1585+++ files/charm/nginx/etc_nginx_nginx.conf 2012-08-21 13:22:29 +0000
1586@@ -0,0 +1,58 @@
1587+user www-data;
1588+worker_processes 4;
1589+pid /var/run/nginx.pid;
1590+
1591+events {
1592+ worker_connections 8192;
1593+ multi_accept on;
1594+ use epoll;
1595+}
1596+
1597+http {
1598+ ##
1599+ # Reverse
1600+ ##
1601+ set_real_ip_from 0.0.0.0/0;
1602+ real_ip_header X-Forwarded-For;
1603+ add_header X-UA-Compatible "IE=Edge,chrome=1";
1604+
1605+ ##
1606+ # Rockin Defaults
1607+ ##
1608+ sendfile on;
1609+ tcp_nopush on;
1610+ tcp_nodelay on;
1611+ keepalive_timeout 5;
1612+ types_hash_max_size 2048;
1613+ server_names_hash_bucket_size 64;
1614+ server_name_in_redirect off;
1615+ client_max_body_size 512k;
1616+ include /etc/nginx/mime.types;
1617+ default_type application/octet-stream;
1618+ index index.php index.html
1619+
1620+ ##
1621+ # Logging Settings
1622+ ##
1623+ access_log /var/log/nginx/access.log;
1624+ error_log /var/log/nginx/error.log;
1625+
1626+ ##
1627+ # Gzip Settings
1628+ ##
1629+ gzip on;
1630+ gzip_disable "msie6";
1631+ gzip_vary on;
1632+ gzip_static on;
1633+ gzip_proxied any;
1634+ gzip_comp_level 4;
1635+ gzip_buffers 16 8k;
1636+ gzip_http_version 1.1;
1637+ gzip_types text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
1638+
1639+ ##
1640+ # Virtual Host Configs
1641+ ##
1642+ include /etc/nginx/conf.d/*.conf;
1643+ include /etc/nginx/sites-enabled/*;
1644+}
1645
1646=== added file 'files/charm/nginx/etc_nginx_sites-enabled_loadbalancer'
1647--- files/charm/nginx/etc_nginx_sites-enabled_loadbalancer 1970-01-01 00:00:00 +0000
1648+++ files/charm/nginx/etc_nginx_sites-enabled_loadbalancer 2012-08-21 13:22:29 +0000
1649@@ -0,0 +1,51 @@
1650+# Default before peer relation is handled
1651+proxy_cache_path /mnt/ramdisk/proxy-cache levels=1:2 keys_zone=proxycache:5m max_size=1000m;
1652+
1653+upstream backend {
1654+ server 127.0.0.1:8080;
1655+}
1656+
1657+server {
1658+ listen 80 default;
1659+ server_name _;
1660+ location / {
1661+ set $no_cache "";
1662+
1663+ if ($request_method !~ ^(GET|HEAD)$) {
1664+ set $no_cache "1";
1665+ }
1666+
1667+ if ($no_cache = "1") {
1668+ add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
1669+ add_header X-Microcachable "0";
1670+ }
1671+
1672+ if ($http_cookie ~* "_mcnc") {
1673+ set $no_cache "1";
1674+ }
1675+
1676+ proxy_no_cache $no_cache;
1677+ proxy_cache_bypass $no_cache;
1678+
1679+ proxy_redirect http://backend /;
1680+ proxy_pass http://backend;
1681+ proxy_cache proxycache;
1682+ proxy_cache_key $scheme$host$request_method$request_uri;
1683+ proxy_cache_valid 200 60s;
1684+ proxy_cache_use_stale updating;
1685+
1686+ proxy_set_header Host $host;
1687+ proxy_set_header X-Real-IP $remote_addr;
1688+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1689+
1690+ proxy_max_temp_file_size 1M;
1691+
1692+ # Custom logging
1693+ log_format custom '$remote_addr - $remote_user [$time_local] '
1694+ '"$request" $status $body_bytes_sent '
1695+ '"$http_referer" "$http_user_agent" nocache:$no_cache';
1696+ access_log /mnt/logs/microcache.log custom;
1697+
1698+ }
1699+}
1700+
1701
1702=== added file 'files/charm/nginx/etc_nginx_sites-enabled_wordpress'
1703--- files/charm/nginx/etc_nginx_sites-enabled_wordpress 1970-01-01 00:00:00 +0000
1704+++ files/charm/nginx/etc_nginx_sites-enabled_wordpress 2012-08-21 13:22:29 +0000
1705@@ -0,0 +1,72 @@
1706+fastcgi_cache_path /mnt/ramdisk/phpfpm-cache levels=1:2 keys_zone=microcache:5M max_size=1G inactive=2h;
1707+
1708+server {
1709+ listen 8080 default;
1710+ server_name _;
1711+ access_log /mnt/access.log;
1712+ error_log /mnt/error.log;
1713+ root /var/www/;
1714+ index index.php index.html;
1715+ client_max_body_size 10m;
1716+ client_body_buffer_size 128k;
1717+ client_header_buffer_size 64k;
1718+
1719+ location = /favicon.ico {
1720+ allow all;
1721+ log_not_found off;
1722+ access_log off;
1723+ }
1724+
1725+ location = /robots.txt {
1726+ allow all;
1727+ log_not_found off;
1728+ access_log off;
1729+ }
1730+
1731+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|zip|gz|tar)$ {
1732+ log_not_found off;
1733+ access_log off;
1734+ expires max;
1735+ }
1736+
1737+ location / {
1738+ # try_files "${document_root}/wp-content/cache/supercache/${host}/${uri}index.html" "${uri}/" "${uri}/index.html" $uri @rewrite;
1739+ try_files $uri $uri/ /index.php?q=$uri&$args;
1740+ }
1741+
1742+ location ~ /\. {
1743+ access_log off;
1744+ log_not_found off;
1745+ deny all;
1746+ }
1747+
1748+ location ~* /(fpm.www.status|fpm.www.ping)$ {
1749+ include /etc/nginx/fastcgi_params;
1750+ fastcgi_pass unix:/var/run/php5-fpm.sock;
1751+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
1752+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
1753+ fastcgi_param QUERY_STRING $args;
1754+ }
1755+
1756+ location ~ \.php$ {
1757+ try_files $uri @rewrite;
1758+ include /etc/nginx/fastcgi_params;
1759+ fastcgi_pass unix:/var/run/php5-fpm.sock;
1760+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
1761+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
1762+ fastcgi_param QUERY_STRING $args;
1763+ }
1764+
1765+ location @rewrite {
1766+ index index.php;
1767+ include /etc/nginx/fastcgi_params;
1768+ fastcgi_pass unix:/var/run/php5-fpm.sock;
1769+ fastcgi_buffers 256 4k;
1770+ fastcgi_intercept_errors on;
1771+ fastcgi_hide_header 'X-Generator';
1772+ fastcgi_read_timeout 14400;
1773+ fastcgi_param SCRIPT_FILENAME $document_root/index.php;
1774+ fastcgi_param SCRIPT_NAME /index.php;
1775+ fastcgi_param QUERY_STRING $args;
1776+ }
1777+}
1778
1779=== added directory 'files/charm/php'
1780=== added file 'files/charm/php/php5-fpm_pool.d_www.conf'
1781--- files/charm/php/php5-fpm_pool.d_www.conf 1970-01-01 00:00:00 +0000
1782+++ files/charm/php/php5-fpm_pool.d_www.conf 2012-08-21 13:22:29 +0000
1783@@ -0,0 +1,41 @@
1784+; -------------------------------------------------------------------- ;
1785+; first pool
1786+; -------------------------------------------------------------------- ;
1787+
1788+[www]
1789+user = www-data
1790+group = www-data
1791+
1792+listen = /var/run/php5-fpm.sock
1793+
1794+request_slowlog_timeout = 10s
1795+slowlog = /mnt/logs/php-fpm/$pool.slowlog.log
1796+
1797+pm = dynamic
1798+pm.max_children = 100
1799+pm.start_servers = 15
1800+pm.min_spare_servers = 4
1801+pm.max_spare_servers = 15
1802+pm.max_requests = 5000
1803+listen.backlog = -1
1804+
1805+; http://url/status
1806+; http://url/status?json
1807+; http://url/status?html
1808+pm.status_path = /fpm.$pool.status
1809+ping.path = /fpm.$pool.ping
1810+ping.response = "pong. $pool OK"
1811+
1812+request_terminate_timeout = 120s
1813+rlimit_files = 131072
1814+rlimit_core = unlimited
1815+catch_workers_output = no
1816+
1817+; this is untested will do this on a later update
1818+
1819+env[HOSTNAME] = $HOSTNAME
1820+env[PATH] = $PATH
1821+env[TMP] = /mnt/tmp
1822+env[TMPDIR] = /mnt/tmp
1823+env[TEMP] = /mnt/tmp
1824+env[OHSO_POOL] = $pool
1825
1826=== added file 'files/charm/php/php5_conf.d_apc.ini'
1827--- files/charm/php/php5_conf.d_apc.ini 1970-01-01 00:00:00 +0000
1828+++ files/charm/php/php5_conf.d_apc.ini 2012-08-21 13:22:29 +0000
1829@@ -0,0 +1,13 @@
1830+extension=apc.so
1831+apc.enable=1
1832+apc.shm_size = 1024M
1833+apc.stat_ctime = 0
1834+apc.stat = 0
1835+apc.max_file_size = 2M
1836+apc.write_lock = true
1837+apc.mmap = /mnt/tmp/apc.XXXXXX
1838+apc.ttl = 0
1839+apc.user_ttl = 7200
1840+apc.gc_ttl = 7200
1841+apc.use_request_time = 1
1842+apc.file_md5 = 1
1843
1844=== added file 'hooks/config-changed'
1845--- hooks/config-changed 1970-01-01 00:00:00 +0000
1846+++ hooks/config-changed 2012-08-21 13:22:29 +0000
1847@@ -0,0 +1,54 @@
1848+#!/bin/bash
1849+
1850+set -ue
1851+
1852+source inc/common
1853+
1854+tuning_level=`config-get tuning`
1855+# Make it lower case
1856+tuning_level=${tuning_level,,}
1857+
1858+wp_content_repo=`config-get wp-content`
1859+expose_info=`config-get debug`
1860+expose_info=${expose_info,,}
1861+
1862+if [ ! -f $config_file_path ]; then
1863+ juju-log "Nothing to configure, since nothing is installed"
1864+ exit 0
1865+fi
1866+
1867+juju-log "Show details? $expose_info"
1868+
1869+if [ "$expose_info" == "yes" ]; then
1870+ rsync -az files/_debug $wp_install_path/
1871+else
1872+ rm -rf $wp_install_path/_debug
1873+fi
1874+
1875+juju-log "I will be using this tuning level: $tuning_level"
1876+
1877+if [ "$tuning_level" == "optimized" ]; then
1878+ # First and foremost, we need to disable the ability to edit
1879+ # themes and upload/update plugins. This breaks a scale-out
1880+ # environment. It's sad but true. If you want to update a plugin
1881+ # install a theme, etc; take a look at the README.
1882+ make_optimized
1883+elif [ "$tuning_level" == "single" ]; then
1884+ # We need to prepare an NFS mount, because someone is probably
1885+ # going to try to scale out. We also need to vamp up caching.
1886+ make_single
1887+elif [ "$tuning_level" == "bare" ]; then
1888+ # Okay, you know what you're doing. You're probably going to
1889+ # use Gluster to stream-line your files, so you don't need to
1890+ # disable anything. We trust you to do what you need to.
1891+ make_bare
1892+else
1893+ juju-log "Not sure about that tuning level."
1894+ exit 1
1895+fi
1896+
1897+do_vcs $wp_content_repo
1898+
1899+chown -R www-data.www-data $wp_install_path
1900+
1901+. hooks/restart
1902
1903=== added symlink 'hooks/db-relation-broken'
1904=== target is u'db-relation-departed'
1905=== modified file 'hooks/db-relation-changed'
1906--- hooks/db-relation-changed 2012-06-18 20:28:16 +0000
1907+++ hooks/db-relation-changed 2012-08-21 13:22:29 +0000
1908@@ -1,5 +1,6 @@
1909 #!/bin/bash
1910
1911+<<<<<<< TREE
1912 set -eu # -x for verbose logging to juju debug-log
1913
1914 UPLOAD_PATH="/var/www/wp-uploads"
1915@@ -9,24 +10,31 @@
1916
1917 # Check we haven't already been setup.
1918 config_file_path="/etc/wordpress/config-default.php"
1919-
1920-# Get the database settings; if not set, wait for this hook to be
1921-# invoked again
1922+=======
1923+set -eu
1924+
1925+juju-log "We've got a db"
1926+
1927+source inc/common
1928+
1929+if [ -f "$config_file_path" ]; then
1930+ # No longer to quietly in to that good night. Update the wp-config file with new DB values
1931+ juju-log "WordPress is already setup, just silently going away"
1932+ exit 0
1933+fi
1934+>>>>>>> MERGE-SOURCE
1935+
1936 database=`relation-get database`
1937-if [ -z "$database" ] ; then
1938- exit 0 # wait for future handshake from database service unit
1939-fi
1940-
1941-# Our protocol on this interface ensures that all or none of the
1942-# settings are set. But we can verify the setting or the values if
1943-# more error checking if desired.
1944 user=`relation-get user`
1945 password=`relation-get password`
1946 host=`relation-get private-address`
1947
1948-# Create an internal secret key for wordpress; this is unrelated to
1949-# the password generated for the admin user of wordpress
1950+if [ -z "$database" ] ; then
1951+ exit 0
1952+fi
1953+
1954 secret_key=`pwgen 10 1`
1955+<<<<<<< TREE
1956
1957 juju-log "Creating appropriate upload paths and directories"
1958 # Setup appropriate upload paths and directories
1959@@ -37,10 +45,39 @@
1960 chmod 0744 $UPLOAD_PATH
1961 chmod 0770 "$UPLOAD_PATH/$hostname"
1962 chown -R root:www-data "/var/www/$hostname/wp-content"
1963+=======
1964+echo $secret_key > wp_secret
1965+source "/usr/share/charm-helper/sh/net.sh"
1966+
1967+##
1968+# OH COME ON WORDPRESS.ORG NO VALID SSL CERTIFICATE?
1969+# Psh, whatever, plain-text for now. #blamenacin
1970+# https://twitter.com/#!/marcoceppi/status/181570187655520260
1971+# I was wrong, we need to use something other than WGET, like cURL
1972+LATEST_WORDPRESS="http://wordpress.org/latest.tar.gz"
1973+DOWNLOAD=`ch_get_file "$LATEST_WORDPRESS" "http://wordpress.org/latest.tar.gz.sha1"`
1974+##
1975+
1976+if [ ! -f "$DOWNLOAD" ] || [ -z "$DOWNLOAD" ]; then
1977+ juju-log "Failed to retrieve $LATEST_WORDPRESS"
1978+ exit 1
1979+fi
1980+
1981+juju-log "Extract ALL THE FILES"
1982+tar -xzf $DOWNLOAD
1983+
1984+mkdir -p $wp_install_path
1985+
1986+juju-log "Move them in to place, but just 'drop' them in place."
1987+rsync -az wordpress/ $wp_install_path
1988+
1989+juju-log "Clean up"
1990+rm -rf wordpress
1991+>>>>>>> MERGE-SOURCE
1992
1993 juju-log "Writing wordpress config file $config_file_path"
1994 # Write the wordpress config
1995-cat > $config_file_path <<EOF
1996+cat > $config_info_path <<EOF
1997 <?php
1998 define('DB_NAME', '$database');
1999 define('DB_USER', '$user');
2000@@ -48,49 +85,60 @@
2001 define('DB_HOST', '$host');
2002 define('SECRET_KEY', '$secret_key');
2003
2004-#This will disable the update notification.
2005-define('WP_CORE_UPDATE', false);
2006+define('WP_CACHE', true);
2007+
2008+/*
2009+define('AUTH_KEY', '$secret_key');
2010+define('SECURE_AUTH_KEY', '$secret_key');
2011+define('LOGGED_IN_KEY', '$secret_key');
2012+define('NONCE_KEY', '$secret_key');
2013+*/
2014
2015 \$table_prefix = 'wp_';
2016-\$server = '$host';
2017-\$loginsql = '$user';
2018-\$passsql = '$password';
2019-\$base = '$database';
2020-\$upload_path = '/var/www/wp-uploads/$hostname';
2021-\$upload_url_path = 'http://$hostname/wp-uploads';
2022-?>
2023-EOF
2024+
2025+EOF
2026+
2027+cat > $config_file_path <<EOF
2028+<?php
2029+
2030+/* That's all, stop editing! Happy blogging. */
2031+
2032+/** Absolute path to the WordPress directory. */
2033+if ( !defined('ABSPATH') )
2034+ define('ABSPATH', dirname(__FILE__) . '/');
2035+
2036+/** Pull in the config information */
2037+require_once(ABSPATH . 'wp-info.php');
2038+require_once(ABSPATH . 'wp-overrides.php');
2039+
2040+/** Sets up WordPress vars and included files. */
2041+require_once(ABSPATH . 'wp-settings.php');
2042+
2043+remove_filter('template_redirect', 'redirect_canonical');
2044+
2045+EOF
2046+
2047 chmod 0644 $config_file_path
2048-
2049-# Write the apache config
2050-# XXX a future branch will change this to use augtool
2051-apache_config_file_path="/etc/apache2/sites-available/$hostname"
2052-juju-log "Writing apache config file $apache_config_file_path"
2053-cat > $apache_config_file_path <<EOF
2054-<VirtualHost *:80>
2055- ServerName $hostname
2056- DocumentRoot /var/www/$hostname
2057- Options All
2058- ErrorLog /var/log/apache2/wp-error.log
2059- TransferLog /var/log/apache2/wp-access.log
2060- # Store uploads in /var/www/wp-uploads/$0
2061- RewriteEngine On
2062- RewriteRule ^/wp-uploads/(.*)$ /var/www/wp-uploads/%%{HTTP_HOST}/\$1
2063-</VirtualHost>
2064-EOF
2065-chmod 0644 $apache_config_file_path
2066-
2067-# Configure apache
2068-juju-log "Enabling apache modules: rewrite, vhost_alias"
2069-a2enmod rewrite
2070-a2enmod vhost_alias
2071+touch $config_override_path
2072+
2073 juju-log "Enabling apache site: $hostname"
2074+<<<<<<< TREE
2075 a2ensite $hostname
2076 a2dissite default
2077
2078 # Restart apache
2079 juju-log "Restarting apache2 service"
2080 /etc/init.d/apache2 restart
2081+=======
2082+
2083+chown -R www-data.www-data $wp_install_path
2084+
2085+. hooks/config-changed
2086+
2087+# Restart nginx
2088+juju-log "Restarting nginx service"
2089+service nginx restart
2090+>>>>>>> MERGE-SOURCE
2091
2092 # Make it publicly visible, once the wordpress service is exposed
2093 open-port 80/tcp
2094
2095=== added file 'hooks/db-relation-departed'
2096--- hooks/db-relation-departed 1970-01-01 00:00:00 +0000
2097+++ hooks/db-relation-departed 2012-08-21 13:22:29 +0000
2098@@ -0,0 +1,5 @@
2099+#!/bin/bash
2100+
2101+source inc/common
2102+
2103+rm -f $config_file_path
2104
2105=== modified file 'hooks/install'
2106--- hooks/install 2011-10-18 06:11:17 +0000
2107+++ hooks/install 2012-08-21 13:22:29 +0000
2108@@ -1,3 +1,58 @@
2109 #!/bin/bash
2110
2111-apt-get -y install wordpress pwgen
2112+add-apt-repository ppa:charmers/charm-helpers
2113+
2114+apt-get update && apt-get -y upgrade
2115+apt-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
2116+
2117+modprobe nfs
2118+
2119+juju-log "Removing PHP Default Cron ..."
2120+rm -f /etc/cron.d/php5
2121+
2122+juju-log "Making /mnt/tmp dir ..."
2123+mkdir -p /mnt/tmp
2124+chmod 1777 /mnt/tmp
2125+
2126+juju-log "Making /mnt/logs ..."
2127+mkdir -p /mnt/logs/php-fpm
2128+chmod -R 1777 /mnt/logs
2129+
2130+## dont make this an actual ramdisk until later when size can be safely tested
2131+## just using plain disk cache for now
2132+juju-log "Making Ramdisk mount point and config ..."
2133+mkdir -p /mnt/ramdisk/proxy-cache
2134+mkdir -p /mnt/ramdisk/phpfpm-cache
2135+chmod -R 1777 /mnt/ramdisk
2136+
2137+juju-log "Cleaning any old or default nginx site configs ..."
2138+rm -f /etc/nginx/sites-enabled/*
2139+rm -f /etc/nginx/conf.d/*
2140+
2141+juju-log "Installing Nginx common config ..."
2142+rm -f /etc/nginx/nginx.conf
2143+install -o root -g root -m 0644 files/charm/nginx/etc_nginx_nginx.conf /etc/nginx/nginx.conf
2144+
2145+juju-log "Installing Nginx actual site config ..."
2146+#rm -f /etc/nginx/sites-available/
2147+install -o root -g root -m 0644 files/charm/nginx/etc_nginx_sites-enabled_wordpress /etc/nginx/sites-available/wordpress
2148+ln -sf ../sites-available/wordpress /etc/nginx/sites-enabled/wordpress
2149+
2150+juju-log "Installing Nginx loadbal config ..."
2151+rm -f /etc/nginx/sites-available/loadbalancer
2152+install -o root -g root -m 0644 files/charm/nginx/etc_nginx_sites-enabled_loadbalancer /etc/nginx/sites-available/loadbalancer
2153+ln -sf ../sites-available/loadbalancer /etc/nginx/sites-enabled/loadbalancer
2154+
2155+juju-log "Installing PHP-FPM pool configs ..."
2156+rm -f /etc/php5/fpm/pool.d/*
2157+install -o root -g root -m 0644 files/charm/php/php5-fpm_pool.d_www.conf /etc/php5/fpm/pool.d/www.conf
2158+
2159+juju-log "Moving PHP and Nginx var dirs to /mnt storage ..."
2160+rsync -avz /var/lib/nginx /mnt/ && rm -rf /var/lib/nginx && ln -s /mnt/nginx /var/lib/
2161+rsync -avz /var/lib/php5 /mnt/ && rm -rf /var/lib/php5 && ln -s /mnt/php5 /var/lib/
2162+
2163+juju-log "Restarting Services ..."
2164+service php5-fpm restart
2165+service nginx restart
2166+
2167+juju-log "So, environment is setup. We'll wait for some hooks to fire off before we get all crazy"
2168
2169=== added symlink 'hooks/loadbalancer-relation-broken'
2170=== target is u'loadbalancer-relation-joined'
2171=== added symlink 'hooks/loadbalancer-relation-departed'
2172=== target is u'loadbalancer-relation-joined'
2173=== added file 'hooks/loadbalancer-relation-joined'
2174--- hooks/loadbalancer-relation-joined 1970-01-01 00:00:00 +0000
2175+++ hooks/loadbalancer-relation-joined 2012-08-21 13:22:29 +0000
2176@@ -0,0 +1,124 @@
2177+#!/usr/bin/env python
2178+#
2179+# reverseproxy-relation-changed - hook for when reverse proxy relation changes
2180+#
2181+# Copyright (C) 2011 Canonical Ltd.
2182+# Author: Clint Byrum <clint.byrum@canonical.com>
2183+#
2184+# This program is free software: you can redistribute it and/or modify
2185+# it under the terms of the GNU General Public License as published by
2186+# the Free Software Foundation, either version 3 of the License, or
2187+# (at your option) any later version.
2188+#
2189+# This program is distributed in the hope that it will be useful,
2190+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2191+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2192+# GNU General Public License for more details.
2193+#
2194+# You should have received a copy of the GNU General Public License
2195+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2196+#
2197+
2198+import sys
2199+import os
2200+import subprocess
2201+import json
2202+import tempfile
2203+import glob
2204+
2205+from socket import getaddrinfo
2206+
2207+remote_unit = os.environ.get("JUJU_REMOTE_UNIT")
2208+
2209+service_name, _ = remote_unit.split("/")
2210+
2211+# TODO: maybe load this from disk for easier customization
2212+t1 = """# Generated by juju
2213+proxy_cache_path /mnt/ramdisk/proxy-cache levels=1:2 keys_zone=proxycache:5m max_size=1000m;
2214+"""
2215+# servers will go here
2216+template = """
2217+server {
2218+ listen 80 default;
2219+ server_name _;
2220+
2221+ location / {
2222+ set $no_cache "";
2223+
2224+ if ($request_method !~ ^(GET|HEAD)$) {
2225+ set $no_cache "1";
2226+ }
2227+
2228+ if ($no_cache = "1") {
2229+ add_header Set-Cookie "_mcnc=1; Max-Age=30; Path=/";
2230+ add_header X-Microcachable "0";
2231+ }
2232+
2233+ if ($http_cookie ~* "_mcnc") {
2234+ set $no_cache "1";
2235+ }
2236+
2237+ proxy_no_cache $no_cache;
2238+ proxy_cache_bypass $no_cache;
2239+
2240+ proxy_redirect http://backend /;
2241+ proxy_pass http://backend;
2242+ proxy_cache proxycache;
2243+ proxy_cache_key $scheme$host$request_method$request_uri;
2244+ proxy_cache_valid 200 60s;
2245+ proxy_cache_use_stale updating;
2246+
2247+ proxy_set_header Host $host;
2248+ proxy_set_header X-Real-IP $remote_addr;
2249+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
2250+ proxy_set_header X-UA-Compatible "IE=Edge,chrome=1";
2251+
2252+ proxy_max_temp_file_size 1M;
2253+ }
2254+}
2255+"""
2256+
2257+units = []
2258+p = subprocess.Popen("relation-list", stdout=subprocess.PIPE)
2259+for unit in p.stdout:
2260+ units.append(unit.strip())
2261+
2262+print units
2263+
2264+servers = """
2265+upstream backend {
2266+ server 127.0.0.1:8080;
2267+"""
2268+for unit in units:
2269+ p = subprocess.Popen(["relation-get", "private-address", unit],
2270+ stdout=subprocess.PIPE, close_fds=True)
2271+ paddress = p.stdout.read().strip()
2272+ p.wait()
2273+ # Add all configured units:
2274+ servers += (" server %s:8080;\n" % (paddress))
2275+servers += '}'
2276+
2277+print servers
2278+
2279+with tempfile.NamedTemporaryFile(dir="/etc/nginx/sites-available/",prefix="loadbalancer", delete=False) as conf:
2280+ conf.write(t1 + servers + template)
2281+ try:
2282+ os.unlink("/etc/nginx/sites-available/loadbalancer.old")
2283+ except:
2284+ pass
2285+ try:
2286+ os.rename("/etc/nginx/sites-available/loadbalancer","/etc/nginx/sites-available/loadbalancer.old")
2287+ except:
2288+ pass
2289+ try:
2290+ os.rename(conf.name, "/etc/nginx/sites-available/loadbalancer")
2291+ except:
2292+ os.unlink(conf.name)
2293+
2294+
2295+
2296+# Just in case haproxy wouldn't start because of empty/bad configs before, start it now
2297+subprocess.call(["service", "nginx", "start"])
2298+subprocess.check_call(["service", "nginx", "reload"])
2299+
2300+subprocess.check_call(["open-port", "80"])
2301
2302=== added symlink 'hooks/nfs-relation-broken'
2303=== target is u'nfs-relation-departed'
2304=== added file 'hooks/nfs-relation-changed'
2305--- hooks/nfs-relation-changed 1970-01-01 00:00:00 +0000
2306+++ hooks/nfs-relation-changed 2012-08-21 13:22:29 +0000
2307@@ -0,0 +1,28 @@
2308+#!/bin/bash
2309+
2310+set -eu
2311+
2312+juju-log "We've got a mount"
2313+source inc/common
2314+
2315+options=`relation-get options`
2316+mountpoint=`relation-get mountpoint`
2317+fstype=`relation-get fstype`
2318+host=`relation-get private-address`
2319+
2320+if [ -z "$fstype" ] ; then
2321+ juju-log "Going to wait for some real data"
2322+ exit 0
2323+fi
2324+
2325+# Write this out to our .nfs-mount file
2326+cat > .nfs-mount <<EOF
2327+#!/bin/bash
2328+MOUNT_TYPE=$fstype
2329+MOUNT_OPS=$options
2330+MOUNT_SERVER=$host
2331+MOUNT_PATH=$mountpoint
2332+
2333+EOF
2334+
2335+. hooks/config-changed
2336
2337=== added file 'hooks/nfs-relation-departed'
2338--- hooks/nfs-relation-departed 1970-01-01 00:00:00 +0000
2339+++ hooks/nfs-relation-departed 2012-08-21 13:22:29 +0000
2340@@ -0,0 +1,20 @@
2341+#!/bin/bash
2342+
2343+set -eu
2344+
2345+juju-log "We've got a mount"
2346+source inc/common
2347+
2348+if [ ! -f .nfs-mount ]; then
2349+ juju-log "I guess we've already done this."
2350+ exit 0
2351+fi
2352+
2353+source .nfs-mount
2354+umount /mnt/wordpress
2355+
2356+rm -f .nfs-mount
2357+
2358+juju-log "Fairwell nfs mount, we hardly knew you"
2359+
2360+. hooks/config-changed
2361
2362=== added file 'hooks/restart'
2363--- hooks/restart 1970-01-01 00:00:00 +0000
2364+++ hooks/restart 2012-08-21 13:22:29 +0000
2365@@ -0,0 +1,4 @@
2366+#!/bin/bash
2367+
2368+. hooks/stop
2369+. hooks/start
2370
2371=== modified file 'hooks/start'
2372--- hooks/start 2011-02-07 22:23:02 +0000
2373+++ hooks/start 2012-08-21 13:22:29 +0000
2374@@ -1,1 +1,4 @@
2375 #!/bin/bash
2376+
2377+service php5-fpm restart
2378+service nginx restart
2379
2380=== modified file 'hooks/stop'
2381--- hooks/stop 2011-10-14 10:22:29 +0000
2382+++ hooks/stop 2012-08-21 13:22:29 +0000
2383@@ -1,3 +1,6 @@
2384 #!/bin/bash
2385-juju-log "Stopping apache"
2386-/etc/init.d/apache2 stop
2387\ No newline at end of file
2388+
2389+service php5-fpm stop
2390+service nginx stop
2391+
2392+
2393
2394=== modified symlink 'hooks/upgrade-charm' (properties changed: -x to +x)
2395=== target was u'install'
2396--- hooks/upgrade-charm 1970-01-01 00:00:00 +0000
2397+++ hooks/upgrade-charm 2012-08-21 13:22:29 +0000
2398@@ -0,0 +1,11 @@
2399+#!/bin/bash
2400+
2401+# Run the install again!
2402+hooks/install
2403+
2404+wp_install_path="/var/www/"
2405+
2406+chown -R www-data.www-data $wp_install_path
2407+
2408+source hooks/config-changed
2409+source hooks/restart
2410
2411=== added directory 'inc'
2412=== added file 'inc/common'
2413--- inc/common 1970-01-01 00:00:00 +0000
2414+++ inc/common 2012-08-21 13:22:29 +0000
2415@@ -0,0 +1,139 @@
2416+#!/bin/bash
2417+
2418+
2419+hostname=`unit-get public-address`
2420+private_name=`hostname -f`
2421+
2422+wp_install_path="/var/www/"
2423+config_file_path="$wp_install_path/wp-config.php"
2424+config_info_path="$wp_install_path/wp-info.php"
2425+config_override_path="$wp_install_path/wp-overrides.php"
2426+
2427+####
2428+## The only hook who should ever remove the NFS mount is the nfs-relation-(departed|broken) hook
2429+## Everyone else should just assume it's there or try to mount it. We don't want to loose people's
2430+## data, they get mad when that happens
2431+####
2432+
2433+make_bare()
2434+{
2435+ # Un-do APC
2436+ rm -f /etc/php5/conf.d/apc.ini
2437+ # IF there is an NFS mount, get everything out of it
2438+ if [ -f .nfs-mount ]; then # Looks like someone got fancy with NFS
2439+ if [ -L $wp_install_path/wp-content ]; then # Check if we actually have a symlink
2440+ rm -f $wp_install_path/wp-content
2441+ rsync -az /mnt/wordpress/wp-content $wp_install_path
2442+ fi
2443+ fi
2444+
2445+ echo "<?php" > $config_override_path
2446+ juju-log "We are now bare"
2447+}
2448+
2449+make_single()
2450+{
2451+ make_bare
2452+ juju-log "Installing PHP apc.ini ..."
2453+ rm -f /etc/php5/conf.d/apc.ini
2454+ install -o root -g root -m 0644 files/charm/php/php5_conf.d_apc.ini /etc/php5/conf.d/apc.ini
2455+ do_nfs
2456+ juju-log "We are now single"
2457+}
2458+
2459+make_optimized()
2460+{
2461+ make_single
2462+ cat > $config_override_path <<EOF
2463+<?php
2464+define('DISALLOW_FILE_MODS', TRUE);
2465+EOF
2466+ juju-log "We are now optimized"
2467+}
2468+
2469+do_nfs()
2470+{
2471+ if [ -f .nfs-mount ]; then
2472+ # This has all the NFS mount stuff in it
2473+ source .nfs-mount
2474+ mkdir -p /mnt/wordpress
2475+ if grep -qs '/mnt/wordpress' /proc/mounts; then
2476+ juju-log "We're already mounted."
2477+ else
2478+ mount -t $MOUNT_TYPE -o $MOUNT_OPS $MOUNT_SERVER:$MOUNT_PATH /mnt/wordpress
2479+ if [ $? -ne 0 ]; then
2480+ juju-log "Could not connect to file-server"
2481+ exit 1 # OH THE HUMANITY OF IT ALL
2482+ fi
2483+ if [ ! -d /mnt/wordpress/wp-content ]; then
2484+ rsync -az $wp_install_path/wp-content /mnt/wordpress/
2485+ fi
2486+
2487+ 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
2488+ ln -s /mnt/wordpress/wp-content $wp_install_path
2489+ juju-log "Mounted NFS"
2490+ fi
2491+ else
2492+ juju-log "There is no nfs mount, not sure what to do, so we'll just bail"
2493+ fi
2494+}
2495+
2496+do_vcs()
2497+{
2498+ local NEW_REPO_URL="$1"
2499+
2500+ if [ -z "$NEW_REPO_URL" ]; then
2501+ rm -f .wp-repo
2502+ return 0
2503+ fi
2504+
2505+ if [ -f .wp-repo ]; then
2506+ source .wp-repo
2507+ else
2508+ REPO_URL=""
2509+ fi
2510+
2511+ if [ "$NEW_REPO_URL" != "$REPO_URL" ]; then
2512+ # We need to determine what kind of URL this is
2513+ if [ $(echo "$NEW_REPO_URL" | egrep -Ec "^(lp:\~|bzr:\/\/|bzr+ssh:\/\/)") -eq 1 ]; then
2514+ local repo_type="bzr"
2515+ local cmd="branch"
2516+ elif [ $(echo "$NEW_REPO_URL" | egrep -Ec "(^git@|^git:\/\/|\.git$)") -gt 0 ]; then
2517+ local repo_type="git"
2518+ local cmd="clone"
2519+ else
2520+ return 1
2521+ fi
2522+
2523+ repo_path=$(mktemp -d)
2524+ $repo_type $cmd $NEW_REPO_URL $repo_path
2525+ # Write all our new data to disk
2526+ cat > .wp-repo <<EOF
2527+#!/bin/bash
2528+REPO_TYPE=$repo_type
2529+REPO_URL=$NEW_REPO_URL
2530+REPO_PATH=$repo_path
2531+REPO_CMD=$cmd
2532+
2533+EOF
2534+ source .wp-repo # Just so the rest of this can run all willy nilly
2535+ fi
2536+
2537+ if [ ! -d "$REPO_PATH" ]; then
2538+ mkdir -p "$REPO_PATH"
2539+ $REPO_TYPE $REPO_CMD $REPO_URL $REPO_PATH
2540+ fi
2541+
2542+ local cwd=$(pwd)
2543+ cd $REPO_PATH
2544+ $REPO_TYPE pull
2545+ cd $cwd
2546+
2547+ # This will clean up anything deleted from wp-content specifically
2548+ rsync -az --delete $REPO_PATH/wp-content/ $wp_install_path/wp-content/
2549+ # This is just incase people have modifications outside of the wp-content directory
2550+ rsync -az $REPO_PATH/ $wp_install_path/
2551+
2552+ # This should be an --exclude flag in the RSYNC command, but I'm not quite sure how to make that work properly.
2553+ rm -rf $wp_install_path/.$REPO_TYPE
2554+}
2555
2556=== modified file 'metadata.yaml'
2557--- metadata.yaml 2012-05-22 22:35:00 +0000
2558+++ metadata.yaml 2012-08-21 13:22:29 +0000
2559@@ -1,16 +1,22 @@
2560 name: wordpress
2561+<<<<<<< TREE
2562 summary: "WordPress is a full featured web blogging tool"
2563 maintainer: Clint Byrum <clint@ubuntu.com>
2564+=======
2565+summary: "WordPress is a full featured web blogging tool, this charm deploys it."
2566+maintainer: Marco Ceppi <marco@ceppi.net>
2567+>>>>>>> MERGE-SOURCE
2568 description: |
2569- WordPress is a full featured web blogging tool:
2570- - Instant publishing (no rebuilding)
2571- - Comment pingback support with spam protection
2572- - Non-crufty URLs
2573- - Themable
2574- - Plugin support
2575+ This will install and setup WordPress optimized to run in the cloud. This install, in particular, will
2576+ place Ngnix and php-fpm configured to scale horizontally with Nginx's reverse proxy
2577 requires:
2578 db:
2579 interface: mysql
2580+ nfs:
2581+ interface: mount
2582 provides:
2583 website:
2584 interface: http
2585+peers:
2586+ loadbalancer:
2587+ interface: reversenginx
2588
2589=== modified file 'revision'
2590--- revision 2012-06-18 20:28:16 +0000
2591+++ revision 2012-08-21 13:22:29 +0000
2592@@ -1,1 +1,5 @@
2593+<<<<<<< TREE
2594 34
2595+=======
2596+78
2597+>>>>>>> MERGE-SOURCE

Subscribers

People subscribed via source and target branches

to all changes: