Graphite Graphs for WordPress

It’s no secret that I love Graphite. My co-workers think it’s a bit ridiculous how much data I feed into our graphite server. I may be a bit excessive, but I have a monitor in my office which allows me to see at a glance every little detail about our network in real time – server health, pageviews, Google Pagespeed Insight scores, etc. I can’t even describe how wonderful a tool it is.

I wanted an easy way to display graphite graphs on the WordPress dashboard. So I rolled out a plugin over the weekend. I give you WordPress Graphite Graphs – I admit I [somewhat] got the idea from WPVIP’s “page generation time” graph in the VIP dashboard, but I’m sure they won’t mind. This plugin allows me (or you) to rapidly deploy monitoring graphs across many different sites without a lot of hassle and NO custom code for each site. Just plug in the URL of your graphite server, the metrics you want to display, and viola – Graphy delight! While I was at it, I went ahead and added support for graphs on the front-end in the sidebar that can be hidden from non-users if needed. Why not, right?

Shoot me a tweet if you’re interested in additional features or get in on the action and fork it on github.

Cambridge, MIT, and WordCamp Boston 2013

I had the pleasure of attending WordCamp Boston 2013 last weekend where I got my third WordCamp T-Shirt for 2013. The camp was held at the Microsoft New England Research & Development (NERD) Center. I also had the pleasure of checking out the MIT campus while I was in Cambridge which was a total treat. Here’s some pictures from the weekend.

A little PHP script to watch for 404 errors

This is a quick script which reads a standard RSS feed and then grabs the response header for each feed item and makes a log file of 404 errors. Useful in debugging sticky situations.

<?php 

$link_queue = array(); 
$exception_queue = array(); 

$timestamp = date( 'Y-m-d H:i:s' ); 
$rss = simplexml_load_file( $argv[1] ); 

foreach ( $rss-&gt;channel-&gt;item as $item )
  array_push( $link_queue, $item-&gt;link );

foreach ( $link_queue as $url ) {
  $handle = curl_init( $url );
  curl_setopt( $handle, CURLOPT_RETURNTRANSFER, TRUE );
  $response = curl_exec( $handle );
  $response_code = curl_getinfo( $handle, CURLINFO_HTTP_CODE );

  if ( $response_code !== 200 )
    array_push( $exception_queue, &quot;{$url} returned status code {$response_code}&quot; );

  curl_close( $handle );
}

if ( count( $exception_queue ) ) {
  foreach ( $exception_queue as $e )
    echo $timestamp . ' &gt; ' . $e . &quot;n&quot;;
} else {
  echo $timestamp . &quot; &gt; All URLs returned status code 200n&quot;;
}

die();

// omit

Throw it in cron like this:
*/30 * * * * php /path/to/script.php http://somesite.com/feed/ >> /path/to/log 2>&1

Or run it on the command line like this:
php /path/to/the/script.php http://somesite.com/feed/

Some Helpful Varnish Commands

Look at incoming requests for URL
varnishlog -c -m RxURL:"/somepage.html"

Look at requests made to the backend for URL
varnishlog -b -m TxURL:"/somepage.html"

Requests for one specific host
varnishlog -c -m RxHeader:"Host: somehost.com"

See cache age for a specific host
varnishlog -c -m RxHeader:"Host: somehost.com" | grep Age

Test VCL compilation
varnishd -C -f /path/to/file.vcl

Ban all URLs
varnishadm -S /path/to/secret -T :6082 "ban.url /"

View Ban List
varnishadm -S /path/to/secret -T :6082 "ban.list"

Real time URLs being sent to backend
varnishtop -b -i TxURL

List backend traffic
varnishlog -b -o

List URLs going to backend
varnishlog -b -o -i TxURL | grep TxURL | awk '{print $4}'

Assign Random Featured Images To All Posts

Could be used for setting up dummy content for a staging environment, or something similar. Be my guest and let me know how you decide to use it in the comments! Also, check my github for this function built into a WP-CLI command.

	$posts = new WP_Query( array( 
		'posts_per_page' => -1, 
		'post_type' => 'post', 
	));
	
	$post_ids = array();
	
	while ( $posts->have_posts() ) {
		$posts->the_post();
		array_push( $post_ids, get_the_ID() );		
	}
	
	wp_reset_query();
	
	$attachments = new WP_Query( array( 
		'posts_per_page' => -1, 
		'post_type' => 'attachment', 
		'post_status' => 'inherit', 
	));
	
	$attachment_ids = array();
	
	while ( $attachments->have_posts() ) {
		$attachments->the_post();
		array_push( $attachment_ids, get_the_ID() );
	}
	
	wp_reset_query();
	
	foreach ( $post_ids as $post_id ) {
		$attachment_id = rand( 0, count( $attachment_ids ) - 1 );
		
		echo 'post:' . $post_id . ' attachment:' . $attachment_ids[$attachment_id];
		
		if ( update_post_meta( $post_id, '_thumbnail_id', $attachment_ids[$attachment_id] ) )
			echo ' SUCCESS';
		else
			echo ' FAILED';
	}
	
	die( 'script complete' );

WordPress Trackback Maintenance

Sometimes you just need to get rid of all that trackback data from your WordPress database. Here’s how to do it manually from your MySQL command line:

DELETE FROM wp_comments WHERE comment_type='trackback';
DELETE FROM wp_comments WHERE comment_type='pingback';

Great, but now your comments counts are all out of whack, since that integer is stored in the wp_posts table. Here’s how to check that:

SELECT wpp.id, wpp.post_title, wpp.comment_count, wpc.cnt
FROM wp_posts wpp
LEFT JOIN (
	SELECT comment_post_id AS c_post_id, count(*) AS cnt FROM wp_comments
	WHERE comment_approved = 1 GROUP BY comment_post_id) wpc
	ON wpp.id=wpc.c_post_id
	WHERE wpp.post_type IN ('post', 'page')
	AND (wpp.comment_count!=wpc.cnt OR (wpp.comment_count != 0 AND wpc.cnt IS NULL)
);

And here’s how to fix it (make a backup of your database first, just in case):

UPDATE wp_posts wpp
LEFT JOIN (
	SELECT comment_post_id AS c_post_id, count(*) AS cnt FROM wp_comments
	WHERE comment_approved = 1 GROUP BY comment_post_id) wpc
	ON wpp.id=wpc.c_post_id
	SET wpp.comment_count=wpc.cnt
	WHERE wpp.post_type IN ('post', 'page')
	AND (wpp.comment_count!=wpc.cnt OR (wpp.comment_count != 0 AND wpc.cnt IS NULL)
);

Cheers!

Making A Bootable CentOS 6.4 x86_64 USB Key

  1. Download the PenDriveLinux “Univerals USB Installer” from http://www.pendrivelinux.com/downloads/Universal-USB-Installer/Universal-USB-Installer-1.9.3.9.exe.
  2. Download the Centos 6.4 Live DVD from http://holmes.umflint.edu/centos/6.4/isos/x86_64/CentOS-6.4-x86_64-LiveDVD.iso (or any other mirror).
  3. Run the Universal USB Installer utility and click “I Agree” on the license agreement screen.
  4. Insert your USB drive into an open USB port on your computer.
  5. Installer Step 1 – Select CentOS from the dropdown list. It’s near the bottom under the “Other Distros Alphabetical” category.
  6. Installer Step 2 – Click “Browse” to locate the ISO. Now, because the utility is trying to locate a specifically named ISO file, you need to type in *.* and hit enter to show all files. Now select your newly downloaded CentOS ISO and click “open”.
  7. Installer Step 3 – Select the drive letter of the USB drive, and finally hit create.
  8. Wait for the utility to work its magic. It will load up 7zG and install the ISO file onto your USB thumb drive.
  9. When it’s done, move the USB key to your target machine, restart, and boot from USB. You’ll now be looking at the CentOS 6.4 live version and you have the option to play with it or install it to disk.

Universal USB Installer7zG

Using Samba on CentOS With Windows 7/8

Go ahead and install the samba packages:

sudo yum install samba samba-client samba-common
smbd --version
sudo chkconfig smb on
sudo chkconfig nmb on
sudo nano /etc/selinux/config

Disable SELINUX by editing /etc/selinux/config and make one small change:

SELINUX=disabled

Make some additions to iptables:

sudo iptables -I INPUT 4 -m state --state NEW -m udp -p udp --dport 137 -j ACCEPT
sudo iptables -I INPUT 5 -m state --state NEW -m udp -p udp --dport 138 -j ACCEPT
sudo iptables -I INPUT 6 -m state --state NEW -m tcp -p tcp --dport 139 -j ACCEPT
sudo service iptables save

NOW, restart the server:

sudo reboot now

Backup and modify smb.conf:

sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
sudo rm /etc/samba/smb.conf
sudo touch /etc/samba/smb.conf
sudo nano /etc/samba/smb.conf

The file should contain:

[global]
workgroup = WORKGROUP
server string = server_name
security = user
map to guest = bad user

[private-share]
path = /media/sdd
valid users = @smbgrp
guest ok = no
writable = yes
browsable = yes

Restart smb and nmb:

sudo service smb restart
sudo service nmb restart

Add your groups and add users to them:

sudo groupadd smbgrp
cd /media/sdb
sudo mkdir secureshare
sudo chown -R username:smbgrp secureshare/
ls -l
sudo chmod -R 0770 secureshare/
sudo usermod -a -G smbgrp username
sudo smbpasswd -a username
sudo service smb restart
sudo service nmb restart
sudo testparm

private-share

Now connect from a windows PC using credentials from CentOS server by right-clicking within “Computer” and selecting “Add a network location”. Follow the prompts and your share will be usable from Windows!

If you cannot connect, it’s most likely the firewall on the CentOS machine. Backup and then flush iptables to test if needed, and then rebuild your firewall accordingly. If you cannot get access to your shares, you have a permissions problem on the server end. Check users, groups, share permissions, and smb.conf for proper values.

You’re welcome 😉

More Goodies For The Boat

I wanted to write an update to my last post about fixing up my boat.

I re-installed the starter, tightened up all the other small issues, and cranked the engine for about ten minutes. Nothing.

BUT … I think I flooded it … because the next day she fired up on the first try, nice and strong, just like she should!

Before it was running, I bought this tool to [attempt to] test the ignition system:

AutoCraft AC664 Ignition TesterTo be fair, the boat has a points ignition system which may somehow differ from other similar systems, but this thing DID NOT do the job. A spark is a spark, and nothing was happening with this tool installed as it should be. My guess? The shaft is pretty loose and difficult to adjust, and coated with some sort of anodized material. They should have made the shaft uncoated copper, aluminum, or nickel. NOT anodized steel.

I know the tool wasn’t working as it should, because the engine actually fired up while it was connected, but this thing still wasn’t sparkin’!

Yesterday, I decided I was sick of wrenching the batteries on and off every time I got on the boat. So, I ordered this:

SAMSUNGThis is a fantastic piece, which allows me to select a single battery, both batteries, or completely disconnect them. It’s rated for 230A continuous and 345A momentary, which should be ample (no pun intended). It also includes a field disconnect, so the switch can be moved with the engine running – which is a great feature. Installation was quick and easy.

So the boat starts, runs, shifts, and (hopefully) floats well. Time to go play! We’re leaving for the Keys tomorrow evening … Cheers!