The Pain and Fury of vmware-cli on CentOS 7, Part 2



Last we left off with a functioning vmware-cli and no way to replicate this in ansible. Lets fix that. We’ll be using FPM to create an RPM containing all of the files that were created by the installer (including cpan modules).
But first, we need to figure out which files need to be packaged. VMWare aaallmost kinda makes this easy for us- each file it installs is tracked in /etc/vmware-vcli/locations (excluding cpan files). Parsing through this, we see

  • a list of all the CPAN modules installed
  • a list of directories it’s created
  • a list of the files it’s created (including symlinks

Step 1: Identifying vmware-cli Files to Package

We’ll hold off on CPAN modules for the moment and jump ahead to directories and files. We can trim this list down quite a bit because we can ignore the filenames in directories that the installer created- in other words, we can include /foo/bar/ and it’ll automatically include /foo/bar/  This allows us to cut down our 1400+ files and directories down to about 63- most of which are symlinks and can be simplified with a wild card. Once we refactor all that info,  the end result is something like this:

/etc/vmware-vcli/locations /usr/share/perl5/VMware/ /usr/share/perl5/WSMan/ /opt/vmwarecli/ /usr/bin/dcli /usr/bin/esxcfg-* /usr/bin/esxcli /usr/bin/resxtop /usr/bin/svmotion /usr/bin/vicfg-* /usr/bin/vifs /usr/bin/vihostupdate /usr/bin/vihostupdate35 /usr/bin/viperl-support /usr/bin/vmkfstools /usr/bin/vmware-cmd /usr/bin/ /etc/vmware-vcli/config

Save this list for later.

An Aside- Thanks for the mess, CPAN.


If you recall, we took a snapshot of our perl modules before and after our installation.  You may remember us doing the following (don’t do this right now!!!):

perl -e 'use doesntexist;'
Can't locate in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at -e line 1.
BEGIN failed--compilation aborted at -e line 
find /usr/local/lib64/perl5 /usr/local

then later:

find /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 >/tmp/final.list

Unfortunately that’s only part of the story. When we rerun that first command:

perl -e 'use doesntexist;'
Can't locate in @INC (@INC contains: /root/perl5/lib/perl5/x86_64-linux-thread-multi /root/perl5/lib/perl5 /root/perl5/lib/perl5/x86_64-linux-thread-multi /root/perl5/lib/perl5 /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at -e line 1.
BEGIN failed--compilation aborted at -e line 1.

What the heck? where did all those /root/perl5/ paths come from? from our /root/.bashrc:

export PERL_MB_OPT="--install_base /root/perl5";
export PERL_MM_OPT="INSTALL_BASE=/root/perl5";
export PERL5LIB="/root/perl5/lib/perl5:$PERL5LIB";
export PATH="/root/perl5/bin:$PATH";

export PERL_MB_OPT="--install_base /root/perl5";
export PERL_MM_OPT="INSTALL_BASE=/root/perl5";
export PERL5LIB="/root/perl5/lib/perl5:$PERL5LIB";
export PATH="/root/perl5/bin:$PATH";

These… these were not here before. So what is in /root/perl5/? The big takeaways are:


*sigh* I have no idea when or why or how these were inserted. I don’t know if that was CPAN not cleaning up after itself or what. all I know is that a script running as a user might now use different libraries than one running as root. I don’t know if those libs are important, but judging from the contents I’m presuming they’re only for building packages and are not functionally involved (I hope).

So we’ll blissfully ignore them and hope it doesn’t bite us later on.

Step 2: Identifying CPAN-Installed Files to Package

ok, so we have our two files- /tmp/pristine.list and /tmp/final.list. We can use the following to gather a list of new files:

cat /tmp/pristine.list /tmp/final.list |sort|uniq -u >/tmp/unique.list

We can do roughly the same thing with this list as we did with /etc/vmware-vcli/locations- ignore the files under any directories we’ve created. In addition, we can ignore any directories we’ve already documented, like /usr/share/perl5/VMware and /usr/share/perl5/WSMan/.  Unfortunately, there are some troublesome directories- /usr/local/share/perl5 and /usr/local/lib64/perl5 are pretty generic- if we include those, it may cause conflicts with other RPMs down the road- I honestly don’t know if this is the case, so we’ll hope for the best. We end up with the following list:


Save this list for later.

Final Packages

Time to bundle everything up… But we’ll need fpm installed first:

yum install rpm-build ruby-devel gcc gem
gem install fpm

From here, we can take those file/directory lists and feed them a single file:

ls -d /etc/vmware-vcli/locations /usr/share/perl5/VMware/ /usr/share/perl5/WSMan/ /opt/vmwarecli/ /usr/bin/dcli /usr/bin/esxcfg-* /usr/bin/esxcli /usr/bin/resxtop /usr/bin/svmotion /usr/bin/vicfg-* /usr/bin/vifs /usr/bin/vihostupdate /usr/bin/vihostupdate35 /usr/bin/viperl-support /usr/bin/vmkfstools /usr/bin/vmware-cmd /usr/bin/ /etc/vmware-vcli/config /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params > /tmp/fpm_final_list

now we’ll feed /tmp/fpm_final_list into FPM to create a shiny-new RPM:

fpm -s dir -d perl-Data-Dumper -a noarch --rpm-user root --rpm-group root -t rpm -n vmware-cli -v '6.0.0_2503617' --iteration 1 -C /  /etc/vmware-vcli/locations /usr/share/perl5/VMware/ /usr/share/perl5/WSMan/ /opt/vmwarecli/ /usr/bin/dcli /usr/bin/esxcfg-* /usr/bin/esxcli /usr/bin/resxtop /usr/bin/svmotion /usr/bin/vicfg-* /usr/bin/vifs /usr/bin/vihostupdate /usr/bin/vihostupdate35 /usr/bin/viperl-support /usr/bin/vmkfstools /usr/bin/vmware-cmd /usr/bin/ /etc/vmware-vcli/config /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params

And because nothing is easy, this throws errors; apparently the current version of FPM doesn’t like symlinks (all of the files in /usr/bin). If we cut those out:

fpm -s dir -d perl-Data-Dumper -a noarch --rpm-user root --rpm-group root -t rpm -n vmware-cli -v '6.0.0_2503617' --iteration=1 -C / /etc/vmware-vcli /opt/vmwarecli /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params /usr/share/perl5/VMware /usr/share/perl5/WSMan


This builds fine, BUT we have no symlinks. Ugh this is kludgy, but we can make a fugly little postinstall script and have FPM include that (I hope)

echo '#!/bin/bash'> /tmp/ ; echo 'cd /opt/vmwarecli/bin/ ; for i in * ; do ln -s /opt/vmwarecli/bin/$i /usr/bin/$i ; done' >>/tmp/
chmod 755 /tmp/

and now we can include it in our fpm command:

fpm -f --post-install /tmp/ -s dir -d perl-Data-Dumper -a noarch --rpm-user root --rpm-group root -t rpm -n vmware-cli -v '6.0.0_2503617' --iteration=1 -C / /etc/vmware-vcli /opt/vmwarecli /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params /usr/share/perl5/VMware /usr/share/perl5/WSMan
no value for epoch is set, defaulting to nil {:level=>:warn}
Force flag given. Overwriting package at vmware-cli-6.0.0_2503617-1.noarch.rpm {:level=>:warn}
no value for epoch is set, defaulting to nil {:level=>:warn}
Created package {:path=>"vmware-cli-6.0.0_2503617-1.noarch.rpm"}

And there we go. Time to test it.

Before we go, we need to snapshot our machine yet again as “package-created.” We also need to copy our wonderful RPM down to our local machine so we can revert back to the raw snapshot and test it.

Testing our RPM

Revert back to our Raw Snapshot, then copy our rpm to /tmp on it.

yum install /tmp/vmware-cli-6.0.0_2503617-1.noarch.rpm

Note that this will install perl-Data-Dumper as a dependency; why? because somehow the rpm was installed and I didn’t notice that it was still there and I don’t want to backtrack to have cpan build it. So now it’s a dependency- we’ll circle back to this in a moment- for now… does it work?

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'


Now that we know our RPM works, we can touch back on some more uncomfortable questions. Right now we have about 28 perl modules installed via CPAN (possibly more). Many of our other apps (such as morgnagplug and manubulon) are dependent on the RPM versions of some of those modules. When we install them, we could have conflicts down the road. So how many of those modules could be installed as RPMs?

module Devel::StackTrace
module Class::Data::Inheritable
module Convert::ASN1
module Crypt::OpenSSL::RSA
module Crypt::X509
module Exception::Class
module UUID::Random
module Archive::Zip
module Compress::Zlib
module Compress::Raw::Zlib
module Path::Class
module Try::Tiny
module Crypt::SSLeay
module IO::Compress::Base
module IO::Compress::Zlib::Constants
module HTML::Parser
module UUID
module Data::Dump
module SOAP::Lite
module URI
module XML::SAX
module XML::NamespaceSupport
module XML::LibXML::Common
module XML::LibXML
module LWP
module LWP::Protocol::https
module IO::Socket::INET6
module Net::INET6Glue

I’m not sure. Let’s find out.

Lets snapshot this as “vmware rpm installed”. As an aside, the snapshot functionality almost makes up for the grief vmware is putting me through.

Which CPAN modules can be swapped for RPMs?

Here’s my plan of attack; for each module listed above,

  1. yum search modulename; Not all modules will have RPMs, but this is the best way to find out if it’s possible to replace.
  2. run rpm -ql vmware-cli |grep modulename ; Note that you should replace the :: with a slash, so Devel::StackTrace becomes Devel/StackTrace
  3. delete matching files from the filesystem (we can always reinstall the RPM if we get in trouble, or even revert back to the “vmware rpm installed” snapshot).
  4. yum install moduleRPM ; this should install ONLY one module, no dependencies; if it has dependencies, put it to the side.

I’m now going to run through each module and document it as I go

  • Devel::StackTrace   RPM exists, has no dependencies. Files removed, rpm installed, vmware-cmd still works
  • Class::Data::Inheritable   RPM exists, has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • Convert::ASN1  RPM exists, has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • Crypt::OpenSSL::RSA RPM exists, HAS DEPENDENCIES, shelved
  • Crypt::X509  No RPM currently exists
  • Exception::Class   RPM exists, has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • UUID::Random No RPM currently exists
  • Archive::Zip  RPM exists, HAS DEPENDENCIES, shelved
  • Compress::Zlib  RPM exists, HAS DEPENDENCIES, shelved
  • Compress::Raw::Zlib   RPM exists, has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • Path::Class No RPM currently exists
  • Try::Tiny RPM exists (perl-try-tiny), has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • Crypt::SSLeay   RPM exists, has no dependencies, Files removed, rpm installed, vmware-cmd still works
  • IO::Compress::Base  No RPM currently exists (probably installed with perl-IO-Compress, but it has dependencies)
  • IO::Compress::Zlib::Constants  No RPM currently exists (possibly installed with perl-IO-Compress, but it has dependencies)
  • HTML::Parser  RPM exists, HAS DEPENDENCIES, shelved (here be dragons)
  • UUID  Difficult to Discern- UUID RPMs doesn’t seem to line up; shelved.
  • Data::Dump No RPM currently exists (not to be confused with Data::Dumper)
  • SOAP::Lite No RPM currently exists
  • URI Difficult to discern… RPM exists, HAS DEPENDENCIES, shelved
  • XML::SAX RPM exists, HAS DEPENDENCIES, shelved
  • XML::NamespaceSupport  RPM exists, has no dependencies. Files removed, rpm installed, vmware-cmd still works
  • XML::LibXML::Common  No RPM currently exists (may be included in a different RPM)
  • XML::LibXML  RPM exists, HAS DEPENDENCIES, shelved
  • LWP  RPM exists, HAS DEPENDENCIES, shelved (here be dragons)
  • LWP::Protocol::https   RPM exists, HAS DEPENDENCIES, shelved (here be dragons)
  • IO::Socket::INET6   RPM exists, HAS DEPENDENCIES, shelved
  • Net::INET6Glue No RPM currently exists (may be included in a different RPM)

Circling back, lets see if any of the libs with dependencies have had their dependencies installed

  • Archive::Zip  RPM exists, has no dependencies. Files removed, rpm installed, vmware-cmd still works

The rest are dead ends. We can now hypothesize that the following libs are acceptable in RPM form:

perl-Devel-StackTrace perl-Class-Data-Inheritable perl-Convert-ASN1 perl-Exception-Class perl-Compress-Raw-Zlib perl-Try-Tiny perl-Crypt-SSLeay perl-XML-NamespaceSupport perl-Archive-Zip

if we exclude the following paths from our FPM build process:

/usr/local/lib64/perl5/auto/Devel/StackTrace/ /usr/local/share/perl5/Devel/ /usr/local/share/perl5/Devel/StackTrace/ /usr/local/lib64/perl5/auto/Class/Data/ /usr/local/share/perl5/Class/Data /usr/local/lib64/perl5/auto/Convert/ /usr/local/share/perl5/Convert/ /usr/local/lib64/perl5/auto/Exception/ /usr/local/share/perl5/Exception/ /usr/local/lib64/perl5/Compress/ /usr/local/lib64/perl5/auto/Compress/ /usr/local/lib64/perl5/auto/Compress/ /usr/local/lib64/perl5/auto/Try/ /usr/local/share/perl5/Try/ /usr/local/lib64/perl5/Crypt/SSLeay/  /usr/local/lib64/perl5/Crypt/  /usr/local/lib64/perl5/auto/Crypt/SSLeay/ /usr/local/lib64/perl5/auto/XML/NamespaceSupport/ /usr/local/share/perl5/XML/ /usr/local/lib64/perl5/auto/Archive/ /usr/local/share/perl5/Archive/


Lets switch over to “package created” Snapshot and repackage our RPM with new dependencies and exclusions:

fpm -f --post-install /tmp/ -s dir -d perl-Data-Dumper -a noarch --rpm-user root --rpm-group root -t rpm -n vmware-cli -v '6.0.0_2503617' --iteration=1 -C / \
-d perl-Devel-StackTrace -d perl-Class-Data-Inheritable -d perl-Convert-ASN1 -d perl-Exception-Class -d perl-Compress-Raw-Zlib -d perl-Try-Tiny -d perl-Crypt-SSLeay -d perl-XML-NamespaceSupport -d perl-Archive-Zip \
-x "**usr/local/lib64/perl5/auto/Devel/StackTrace/**" -x "**usr/local/share/perl5/Devel/" -x "**usr/local/share/perl5/Devel/StackTrace/**" -x "**usr/local/lib64/perl5/auto/Class/Data/**" -x "**usr/local/share/perl5/Class/Data**" -x "**usr/local/lib64/perl5/auto/Convert/**" -x "**usr/local/share/perl5/Convert/**" -x "**usr/local/lib64/perl5/auto/Exception/**" -x "**usr/local/share/perl5/Exception/**" -x "**usr/local/lib64/perl5/Compress/**" -x "**usr/local/lib64/perl5/auto/Compress/**" -x "**usr/local/lib64/perl5/auto/Compress/**" -x "**usr/local/lib64/perl5/auto/Try/**" -x "**usr/local/share/perl5/Try/**" -x "**usr/local/lib64/perl5/Crypt/SSLeay/**" -x "**usr/local/lib64/perl5/Crypt/" -x "**usr/local/lib64/perl5/auto/Crypt/SSLeay/**" -x "**usr/local/lib64/perl5/auto/XML/NamespaceSupport/**" -x "**usr/local/share/perl5/XML/" -x "**usr/local/lib64/perl5/auto/Archive/**" -x "**usr/local/share/perl5/Archive/**" \
/etc/vmware-vcli /opt/vmwarecli /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params /usr/share/perl5/VMware /usr/share/perl5/WSMan

Note: fpm’s excludes are picky as hell- you need doublequotes around each one as well as double wildcards in place of a leading / and trailing it if a directory.

Even then, this STILL leaves the directories, just not the contents of the directory.

I’m trying too hard. Let’s try something slightly different:

fpm -f --post-install /tmp/ -s dir -d perl-Data-Dumper -a noarch --rpm-user root --rpm-group root -t rpm -n vmware-cli -v '6.0.0_2503617' --iteration=1 -C / \
-d perl-Devel-StackTrace -d perl-Class-Data-Inheritable -d perl-Convert-ASN1 -d perl-Exception-Class -d perl-Compress-Raw-Zlib -d perl-Try-Tiny -d perl-Crypt-SSLeay -d perl-XML-NamespaceSupport -d perl-Archive-Zip \
-x "**Devel/StackTrace**" -x "**Class/Data/Inheritable**" -x "**Convert/ASN1**" -x "**Exception/Class**" -x "**Compress/Raw/Zlib**" -x "**Try/Tiny**" -x "**Crypt/SSLeay**" -x "**XML/NamespaceSupport**" -x "**Archive/Zip**" \
/etc/vmware-vcli /opt/vmwarecli /usr/lib64/perl5/auto/Locale /usr/lib64/perl5/auto/Params /usr/lib64/perl5/perllocal.pod /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/perl5/Locale/Maketext /usr/share/perl5/Params /usr/share/perl5/VMware /usr/share/perl5/WSMan

Jackpot. None of the replaced modules are in the RPM, but it did reveal something else…

VMware didn’t track the dependencies that CPAN built, only the ones it needed. What do I mean? For example, /usr/local/lib64/perl5/Class/ appears in our RPM bundle and our final.list.  It was installed while vmware-cli was installing. but there is no trace of Class::MethodMaker in /etc/vmware-vcli/locations.

That means there’s an entirely new group of CPAN modules that exist that we probably don’t need. We’ll put this on the back burner and circle back later (maybe). In the meantime we need to switch over to our Raw snapshot and test this new RPM. Make sure to copy it to your local machine before rebooting.

Once it’s copied over, try installing it again:

yum install /tmp/vmware-cli-6.0.0_2503617-1.noarch.rpm
Install 1 Package (+10 Dependent packages)

It should be our friendly new RPMs.

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'



At this point, our RPM is installing the following CPAN modules (based on packlist files):



I’m positive we could trim out many if not most of these, but I’m exhausted. I still need to get the rest of my stuff installed.


If this was helpful, please leave a comment below.

The Pain and Fury of vmware-cli on CentOS 7


ok, this has reached the point of absurdity that I need to document it.

The Goal

Install vmware-cli on CentOS 7 for to use.

The Requirements

  • installation needs to be reproducible with ansible.
  • the latest version of the cli, VMware-vSphere-CLI-6.0.0-2503617.x86_64.tar.gz

The Complications

  1. CentOS 7 uses a new version of Perl that is binary incompatible.
  2. VMware doesn’t pay attention.
  3. CPAN seemed like a great idea 22 years ago. Unfortunately it didn’t keep up with the times.

The Setup

  • Freshly installed CentOS 7
  • The following custom ansible packages are installed (to give you an idea of what’s I was starting with there without absurd detail):
    • vim
    • sudo
    • sshkeys
    • bashconfig
    • snmpd
    • mariadb
    • httpd
    • git
    • icinga2(nagios-plugins,nagios-plugins-all )
    • manubulonplugins (perl-Net-SNMP)
    • morgnagplug (perl-HTML-Parser,perl-Compress-Raw-Zlib,perl-IO-Zlib,perl-libxml-perl,perl-XML-LibXML,perl-Time-Duration,perl-Number-Format,perl-Config-IniFiles,perl-DateTime)
  • CPAN is NOT installed (it’s a PITA with ansible, so it’s a last resort).

I have currently have two snapshots:

  • a barebones snapshot without all this stuff installed (but it takes 20 minutes to reinstall it all). (Raw Snapshot)
  • a snapshot with all of the stuff listed above installed. (Configured Snapshot)

Test 1: Simplest possible install using –default

The initial installation demands openssl-devel be installed (I’m skipping that hassle for brevity).

yum install openssl-devel
./ --prefix=/opt/vmwarecli EULA_AGREED=yes --default

This will select the perl modules pre-packaged by VMware. Note that it does complain about the following:

MIME::Base64 3.14 or newer 
Try::Tiny 0.22 or newer 
Socket6 0.23 or newer

Which are installed via RPM for net-snmp and other packages and are non-negotiable. It installs fine, except it doesn’t work:

 /opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'
Hiding the command line arguments: symbol lookup error: /usr/lib64/perl5/auto/Crypt/SSLeay/ undefined symbol: Perl_Gthr_key_ptr

This is where Complication #1 comes into play- was compiled against a different system, so it’s incompatible. That’s a simple fix, right?

yum install perl-Crypt-SSLeay
./ --prefix=/opt/vmwarecli EULA_AGREED=yes --default
/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'
 length() used on @array (did you mean "scalar(@array)"?) at /usr/lib64/perl5/IO/Compress/Zlib/ line 198. SOAP request error - possibly a protocol issue: 500 read timeout

out of the frying pan and into the fire. There’s two actual problems here:

  1. The pre-packaged modules include a broken copy of IO::Compress::Zlib. This is annoying, but it doesn’t stop anything (I don’t think).
  2. There’s a problem with reading the soap stuff. what problem? Not sure yet.

That SOAP error first appears in 2009. it ties back to an old version of LWP (aka perl-libwww-perl.rpm). This is installed because morgnagplug uses perl-XML-LibXML.

OK, fine. I can break morgnagplug for the time being (testing purposes), so I remove that and rerun the installer. This pulls down the vmware-cli pre-packaged version of LWP and libxml (note that perl-libxml-perl.noarch.rpm is still installed through all of this). I’ve also removed it from my

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'
/usr/bin/perl: symbol lookup error: /usr/lib64/perl5/auto/XML/LibXML/Common/ undefined symbol: Perl_Gthr_key_ptr

So Now I’m at an impasse- two of the vmware prepackaged modules are not binary compatible- while I could work around Crypt::SSLeay, I couldn’t work around XML::LibXML because it’s dependent on perl-libwww-perl (LWP), which is incompatible.

At this point I can attempt to track down older versions of perl-libwww-perl, but that will then require older RPMs of half a dozen other libraries. This will require pinning them so they’re not accidentally upgraded and ignoring any security patches that may appear. Obviously this is not acceptable.

I don’t think I can go any further down this road. Time to consider CPAN.

Test 2: Maximum CPAN

I roll back to my Configured snapshot, then install openssl-devel like before, then cpan and friends. This includes libuuid-devel for UUID and gcc (!) to get everything CPAN wants to install first try. Note this was about 3 days of trial and error to figure out what was missing so that everything would install properly (mostly because of UUID).

yum install openssl-devel libuuid-devel perl-YAML perl-Devel-CheckLib gcc perl-CPAN

Just as an FYI, CPAN is dependent on perl-ExtUtils-Install, perl-ExtUtils-MakeMaker, perl-ExtUtils-Manifest, perl-ExtUtils-ParseXS, perl-Test-Harness, perl-devel, and perl-local-lib among other things. This is important in a moment (Note: RPM installed perl-ExtUtils-MakeMaker-6.68-3.el7.noarch)

./ --prefix=/opt/vmwarecli EULA_AGREED=yes
Do you want to install precompiled Perl modules for RHEL?
[yes] no
Can't locate Module/ in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .).
BEGIN failed--compilation aborted.
Below mentioned modules with their version needed to be installed,
these modules are available in your system but vCLI need specific 
version to run properly

Module: ExtUtils::MakeMaker, Version: 6.96 
Module: Module::Build, Version: 0.4205 
Module: LWP, Version: 5.837 
Do you want to continue? (yes/no)

Note that the compilation failure above is due to a missing Module::Build, which it looks like it’ll install below anyways.  Also note the three packages that need explicit versions-

  • perl-ExtUtils-MakeMaker 6.68 vs ExtUtils::MakeMaker 6.96 (Newer than RPM)
  • perl-Module-Build 0.40.05 vs Module::Build 0.4205 (Newer than RPM)
  • perl-libwww-perl 6.05 vs LWP 5.837 (Older than RPM)

In either case, this fails because Class::MethodMaker can’t be built and the script fails:

CPAN not able to install following Perl modules on the system. These must be 
installed manually for use by vSphere CLI:

Class::MethodMaker 2.10 or newer

Following it’s directions, I try to install it manually to see what it’s missing:

 cpan -i Class::MethodMaker
Reading '/root/.cpan/Metadata'
 Database was generated on Tue, 19 May 2015 16:17:02 GMT
Running install for module 'Class::MethodMaker'
Running make for S/SC/SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz
Checksum for /root/.cpan/sources/authors/id/S/SC/SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz ok Building S/SC/SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz

Checking if your kit is complete...
Looks good
JSON::PP 2.27103 is not available
 at /usr/local/share/perl5/CPAN/Meta/ line 22.
 at /usr/local/share/perl5/ExtUtils/ line 831.
JSON::PP 2.27103 is not available
 at /usr/local/share/perl5/CPAN/Meta/ line 22.
Warning: No success on command[/usr/bin/perl Makefile.PL]
 /usr/bin/perl Makefile.PL -- NOT OK
Running make test
 Make had some problems, won't test
Running make install
 Make had some problems, won't install
Could not read metadata file. Falling back to other methods to determine prerequisites

So CPAN fails because it requires JSON::PP; I have three choices- install JSON::PP via CPAN, install JSON::PP via RPM or install Class::MethodMaker via RPM. I don’t want to get into dependency hell with cpan, and I know from previous experience that it will complain that the rpm for JSON::PP is too old, so I’ll attempt to simply install the perl-Class-MethodMaker rpm (which oddly isn’t dependent on JSON::PP).  I’ll add this to my “always install first” list with openssl-devel.

(This is especially frustrating because I *know* that the installer will eventually use cpan to install JSON::PP, and this error is likely caused by poor dependency ordering.)

This works, and I’m able to run the install script again; this time with only two problematic modules:

The following Perl modules were found on the system but may be too old to work 
with vSphere CLI:

MIME::Base64 3.14 or newer 
Socket6 0.23 or newer

The real question- will vmware-cmd work?

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'

And that’s a no-go. It’ll hang for 5 minutes or so before erroring. I can tell 20 seconds in that this is the case, but for certainty I have to wait for the timeout before I can continue. What I get is even more interesting:

SOAP request error - possibly a protocol issue: <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc=""

Well what the hell. 4 screens full of solid xml code. Looking over it I see snippets like this:

<summary>ISO [Datastore 1] ISOs/SW_DVD9_Windows_Svr_Std_and_DataCtr_2012_R2_64Bit_English_-4_MLF_X19-82891.ISO</summary>

Which means at least it’s getting through.

So I google for that error and find this page, which indicates Net::HTTP needs to be an older version just like LWP.  The thing is I’m not intentionally installing the perl-Net-HTTP RPM; it’s installed by morgnagplug along with perl-XML-LibXML,  perl-XML-SAX and perl-XML-LibXML… so I guess I uninstall those four RPMs and rerun the installer? This is getting confusing. I suspect I should reimage this machine without morgnagplug and see if that makes things easier. I’ll save that for the next attempt.

yum remove perl-Net-HTTP perl-XML-LibXML perl-XML-SAX perl-libwww-perl
./ --prefix=/opt/vmwarecli EULA_AGREED=yes
CPAN not able to install following Perl modules on the system. These must be 
installed manually for use by vSphere CLI:

XML::LibXML::Common 0.13 or newer 
XML::LibXML 1.63 or newer


cpan -i XML::LibXML
Checking for ability to link against libxml2...libxml2, zlib, and/or the Math library (-lm) have not been found.
Try setting LIBS and INC values on the command line
Or get libxml2 from
If you install via RPMs, make sure you also install the -devel
RPMs, as this is where the headers (.h files) are.

Also, you may try to run perl Makefile.PL with the DEBUG=1 parameter
to see the exact reason why the detection of libxml2 installation
failed or why Makefile.PL was not able to compile a test program.
No 'Makefile' created SHLOMIF/XML-LibXML-2.0121.tar.gz

If I read that correctly, cpan won’t build it because the libxml2-devel rpm is not installed.

yum install libxml2-devel.x86_64
cpan -i XML::LibXML
Warning: prerequisite XML::SAX 0.11 not found.
JSON::PP 2.27103 is not available
 at /usr/local/share/perl5/CPAN/Meta/ line 22.
 at /usr/local/share/perl5/ExtUtils/ line 831.
JSON::PP 2.27103 is not available
 at /usr/local/share/perl5/CPAN/Meta/ line 22.
Warning: No success on command[/usr/bin/perl Makefile.PL]
 /usr/bin/perl Makefile.PL -- NOT OK

damnit. Looks like I’m going down the CPAN rabbit hole.

cpan -i JSON::PP
cpan -i XML::SAX
t/21saxini.t ..... Can't locate in @INC (@INC contains: /root/.cpan/build/XML-SAX-0.99-OJOmqR/blib/lib /root/.cpan/build/XML-SAX-0.99-OJOmqR/blib/arch /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at t/21saxini.t line 6.
BEGIN failed--compilation aborted at t/21saxini.t line 6.
t/21saxini.t ..... Dubious, test returned 2 (wstat 512, 0x200)
No subtests run 
t/40cdata.t ...... ok 
t/42entities.t ... ok 
t/99cleanup.t .... ok 

Test Summary Report
t/21saxini.t (Wstat: 512 Tests: 0 Failed: 0)
 Non-zero exit status: 2
 Parse errors: No plan found in TAP output
Files=14, Tests=98, 2 wallclock secs ( 0.11 usr 0.02 sys + 2.07 cusr 0.16 csys = 2.36 CPU)
Result: FAIL
Failed 1/14 test programs. 0/98 subtests failed.
make: *** [test_dynamic] Error 255
 /bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
 reports GRANTM/XML-SAX-0.99.tar.gz
Running make install
 make test had returned bad status, won't install without force

what? Fine.

cpan -i Fatal
cpan -i XML::SAX
cpan -i XML::LibXML


ok… where was I? Oh yes.

./ --prefix=/opt/vmwarecli EULA_AGREED=yes

It runs and complains about MIME::Base64 and Socket6 again.


/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'
SOAP request error - possibly a protocol issue: <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc=""

and it takes forever again… not a good sign… aaand another giant block of xml.

Searching again finds this page.

Again pointing out that Net::HTTP is the source of the problem…. but I uninstalled the bad version and let the installer install the correct one, right?

 perl -MNet::HTTP -le 'print $Net::HTTP::VERSION'
perl -e 'use LWP; print LWP->VERSION."\n"'


OK… so lets install specific friggen version manually.  the installer mentions LWP 5.837

cpan -i GAAS/libwww-perl-5.837.tar.gz
perl -MNet::HTTP -le 'print $Net::HTTP::VERSION'
perl -e 'use LWP; print LWP->VERSION."\n"'

ok, 17th try is a charm…

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'


… so you remember how we did that, right?

It’s SOOOO tempting to throw my hand up, say done, and walk away, but it doesn’t fit our first requirement- reproducible from ansible.

Remember, you can’t use *cpan* from ansible. Sure, you can use cpanm, but not cpan.


Now that I roughly know what I need, I’m going to go back to the Raw Snapshot capture the state of all my perl module directories, reinstall vmware-cli, then bundle them all into a single RPM with FPM. Why? Because CPAN will not be needed at that point, and I will be able to rebuild that state much easier.

Test 3: Clean Slate and FPM

Clean slate sucks because I don’t have sudo set up along with a slew of other useful things. Oh well. First things first- lets get a list of all of our current perl modules

perl -e 'use doesntexist;'
Can't locate in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at -e line 1.
BEGIN failed--compilation aborted at -e line 
find /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 >/tmp/pristine.list

Some of those dirs don’t even exist yet, but it’s still important to track them.

I copy the vmware folder back over, and begin installing all of my prereqs. At this point it’s only a few things:

yum install openssl-devel
yum install libuuid-devel perl-YAML perl-Devel-CheckLib gcc perl-CPAN libxml2-devel.x86_64

Next we’re gonna run the installation script. I’m pretty sure that it’ll fail, but at least it’ll configure CPAN the way it needs it rather than me bumbling through it on the first run.

./ --prefix=/opt/vmwarecli EULA_AGREED=yes
Do you want to install precompiled Perl modules for RHEL?
[yes] no

Can't locate Module/ in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .).
BEGIN failed--compilation aborted.
Can't locate in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .).
BEGIN failed--compilation aborted.
Below mentioned modules with their version needed to be installed,
these modules are available in your system but vCLI need specific 
version to run properly

Module: ExtUtils::MakeMaker, Version: 6.96 
Module: Module::Build, Version: 0.4205 
Module: LWP, Version: 5.837 
Do you want to continue? (yes/no) yes

CPAN not able to install following Perl modules on the system. These must be 
installed manually for use by vSphere CLI:

Class::MethodMaker 2.10 or newer

OK, now that CPAN is installed, I should be able to simply install these two:

cpan -i JSON::PP
cpan -i Fatal
cpan -i Class::MethodMaker
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/array.t .............. Can't locate in @INC (@INC contains: /root/.cpan/build/Class-MethodMaker-2.24-hloQDv/t /root/.cpan/build/Class-MethodMaker-2.24-hloQDv/blib/lib /root/.cpan/build/Class-MethodMaker-2.24-hloQDv/blib/arch /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at /root/.cpan/build/Class-MethodMaker-2.24-hloQDv/t/ line 135.
BEGIN failed--compilation aborted at /root/.cpan/build/Class-MethodMaker-2.24-hloQDv/t/ line 135.
Compilation failed in require at t/array.t line 22.
BEGIN failed--compilation aborted at t/array.t line 22.
t/array.t .............. Dubious, test returned 2 (wstat 512, 0x200)
Files=8, Tests=0, 1 wallclock secs ( 0.04 usr 0.00 sys + 0.44 cusr 0.06 csys = 0.54 CPU)
Result: FAIL
Failed 8/8 test programs. 0/0 subtests failed.
make: *** [test_dynamic] Error 2
 /bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
 reports SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz
Running make install
 make test had returned bad status, won't install without force

Oh FFS. Maybe there’s an Env package I’m unfamiliar with that’s needed? I thought that was part of perl core… apparently not.

cpan -i Env
cpan -i Class::MethodMaker

and rerun the installer again…

./ --prefix=/opt/vmwarecli EULA_AGREED=yes
Do you want to install precompiled Perl modules for RHEL?
[yes] no


/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'


Make a quick Snapshot to give us a recovery point.

Cleaning up

While there are many things we need to clean up, there’s one thing I want to get out of the way quickly- removing some of the cruft we installed to get CPAN to work. Unlike Debian’s apt, yum doesn’t notify you when dependencies are no longer needed, so I browed /var/log/yum.log and reviewed all of the packages installed while we were doing this. Here’s the list I came up with:

yum remove libcom_err-devel libsepol-devel zlib-devel keyutils-libs-devel pcre-devel libselinux-devel libverto-devel krb5-devel openssl-devel mpfr libmpc cpp kernel-headers glibc-headers glibc-devel libdb-devel perl-local-lib perl-Digest perl-Digest-SHA gdbm-devel perl-Test-Harness pyparsing systemtap-sdt-devel perl-ExtUtils-Manifest perl-ExtUtils-Install perl-ExtUtils-MakeMaker perl-ExtUtils-ParseXS perl-devel perl-CPAN gcc perl-YAML libuuid-devel perl-Devel-CheckLib xz-devel libxml2-devel

After removing that, we

/opt/vmwarecli/bin/vmware-cmd --server -l -h esxhost1 --username --password 'somethingsecure'

Excellent- everything seems to be working.

Lets take one last snapshot of our perl modules for later comparison to pristine.list:

find /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 >/tmp/final.list


We now have a functioning vmware-cmd, but it STILL doesn’t meet our primary requirement- it must work via ansible.


We’ll pick up the rest in Part 2.


Change Fatigue


It’s not that I fear change, it’s that I’m weary of it.

This is a good example. I was recently notified that a new version of my operating system (Kubuntu) was release.

After upgrading, here is the list of things that needed to be fixed, in the order that I found them:

  • grub timeout needs to be reset to 1 second
  • log in and see all of my settings, widgets, backgrounds, etc are gone.
  • Rage because the text in konsole is almost illegible
  • Go to control panel
    • Go to look and feel
      • change desktop theme back to Oxygen, because I *think* that’s what I had. Still doesn’t look right
      • Switch cursor theme to Oxygen white. Looks better, but not sure if it’s the same one. I notice that this doesn’t affect the cursor when it’s over the desktop, just over the system settings box.
      • Switch splash screen to none because the new one is Ugly and I see no other options.
    • Go to Colors
      • try various schemes, none look right
      • try “get new schemes” to find it doesn’t work
      • go with Oxygen because it might be the one I had
    • Go to Fonts
      • switch all the damn fonts back to ubuntu (I recall this being the previou default)
    • Go to Icons
      • Switch the theme back to Oxygen, which is what I think I had
    • Rage because my cursor keeps getting bumped and moved because of the touchpad sensitivity being changed
    • Skip down to Input Devices
      • click on touchpad
        • go to scrolling
          • disable edge scrolling
          • disable twofinger scrolling
        • go to sensitivity
          • enable palm detection
          • make random decreases to width and pressure settings because you can’t tell if that fixed the problem
        • Go to Taps, set all corners to no action, disable tap and drag gestures because I don’t think that’s there.
    • Go Desktop Behavior
      • go to desktop effects
        • disable zoom, background contrast, blur, fade, login, logout, maximize, screen edge, sliding popups, translucency, minimize animation, slide, desktop grid, and present windows because they’re all annoying.
  • I cannot find “change desktop image” under System Settings despite having clicked on numerous things labeled “desktop” and “workspace”
  • right click on desktop to go to desktop settings
    • notice that all of the preloaded images have generic preview icons.
    • click through and apply each one trying to find my wallpaper I set 26 months ago.
    • Try to open and find the damn thing in my directory
    • find the Open Image dialog doesn’t behave *at all*. sidebar shortcuts do not open to the right places, it’s slow, laggy, misaligned and a general clusterfuck.
  • Find and edit /etc/default/grub and switch timeout to 1 second, then run “update-grub”

Hope that this is a weird fever dream and reboot.


After a second reboot, it’s slightly more responsive.

  • re-add panel on second monitor
  • re-add task manager to second panel
  • re-enable “only show tasks from current screen”
  • try changing wallpaper again, previews still broken, dialog broken.
  • Open Konsole and set the shell profile font back to droid sans mono 10
  • Attempt to start virtualbox, get error that drivers need to be rebuilt

I’ve to re-add my desktop widgets (memory usage, cpu, weather, etc), but they appear to be gone now.

  • get error that headers are missing
  • switch back to classic application K menu


Overall it was disappointing and frustrating, and I wish I hadn’t. While I understand it’s mainly due to the new version of plasma, it simply wasn’t ready for release. I need to get stuff done, and I can’t wait for this to be fixed.

Update: Since writing this, I’ve decided to re-image my machine using mint with KDE. So far, it’s much better than 15.04.


Yak Shaving: VMWare Update Edition.

  1. Review nessus report, see Samba needs patching.
  2. Patch Samba.
  3. While retesting, I notice ESX has a patch that needs implementing.
  4. Find out they released 6.0 today. Rather than upgrading to 5.5.1 then 6.0, I look into upgrading directly to 6.0
  5. While looking to implement that I research updatemanager, which I can’t use since I don’t have a windows server to install it on.
  6. So I look at doing it manually, and find out that I need to upgrade vcenter first, since vcenter can’t manage esx hosts that are a higher version.
  7. I find which seems to be exactly what I need.
  8. Spend half an hour looking for OVA file similar to what was used for 5.5. It does not exist.
  9. identify vcenter appliance download for 6.0. Download 3 gig ISO. Not an exact match, but close.
  10. mount ISO locally. Setup file says “vCenter Server Appliance installer cannot run on Linux. It must be run on Windows.”
  11. Spend half an hour searching for the friggen OVA.
  12. Someone clues me in that “vcsa/vmware-vcsa” on the iso is actually the OVA file. Copy that from readonly ISO to local disk, rename it as vmware-vsca-6.0.ova
  13. go to vcenter server web client, navigate to datastore.
  14. See coworker set off alarms on one datastore for being overused. Need to look into that later.
  15. Find out that I need to install a browser plugin to upload a friggen file to their web interface.
  16. Download plugin, install it.
  17. plugin doesn’t appear, realize that it installed in the wrong place.
  18. research how to uninstall the stupid plugin, then reinstall it to the right place. Still doesn’t show.
  19. Someone suggests using OVAtool. I don’t even remember what that does or if it’ll even help me. I don’t know if it works on linux, if I can install it on my workstation, or where to even find it.
  20. restart chrome; lose half of my tabs when the second window doesn’t reappear. plugin still doesn’t work.

Day two:

  1. retry chrome plugin, it fails to be detected again.
  2. research to find out that the plugin interface that VMware uses is deprecated, and their plugin only works with archaic versions of chrome
  3. Give up, load windows VM
  4. download the *deprecated* vsphere client for windows
  5. attempt to install vsphere. Installer disappears.
  6. try reinstalling, receive error that installer is in progress, then fails, then receive another failure message regarding .net 3.5
  7. installer refuses to run because an installer is already running.
  8. reboot windows
  9. run installer, installer disappears. Task manager shows background process “windows modules installer worker” using 99% of my disk bandwidth. maybe it’s still working?
  10. after 10 minutes, installer reappears. entire install takes 25 minutes.
  11. Upload OVA to datastore1 so it can be deployed.
  12. Attempt to deploy the OVA template through the web interface. Notified that “The CLient Integration Plugin must be installed to enable OVF functionality.”  (note OVA and OVF are interchangeable at this point.)
  13. Unable to COPY said text from web interface because hell, why not. Text is not selectable; maybe it’s an image?
  14. Attempt to create a new virtual machine from OVA template. Datastores inaccessible.
  15. Attempt to deploy OVA from windows client. Am unable to deploy from datastore (i.e. I must reupload 1.8 gig file again).
  16. Upload template, click through menus and get a brand new “fill in the blank” screen that I don’t recall seeing before. Attempt to fill it out to the best of my ability.
  17. Start Appliance, fails due to password needing to be reset. Web interface does not respond.
  18. After 20 minutes of digging, I delete it.
  19. start over, leaving the form empty.
  20. Similar Message: “Root password is not set. vmdir.password is not set; aborting installation.” Web interface does not respond.
  21. review my notes from the 5.5 install.
  22. dry run installation of 5.5 OVA file;  existing form has 5 fields.; Hostname is the only one really required.
  23. 6.0 OVA has 46 fields; It is unclear how many are required. Perhaps all of them.
    1. if host network mode is set to DHCP, ip address and host network prefix are not required. Default gateway, dns servers, and host identity don’t state if they are required.
    2. SSO Configuration talks about a directory password for replication partner. Is this the 5.5 instance I’m planning to mirror? I don’t think so- I’m pretending this is a stand-alone instance so I can follow the migration video later, which presumes the new instance is already installed but not configured. Setting temporary password for administrator.
    3. leaving the rest of the SSO configuration default
    4. leave database config set to Embedded.
    5. Setting root password in System Configuration (which is different than the Administrator account password set 3 steps ago).
    6. Leaving upgrade configuration blank.
    7. Leave networking properties blank
  24. After finally getting the vcenter 6.0.0 installed, it turns out 6.0.0 no longer uses port 5480, as seen in the first video, which was the only one that came up when searching for upgrades yesterday.
  25. Because, why would the upgrade process from 5.1 to 5.5 be the same as 5.5 to 6.0, right?
  26. Start searching again, find this video which appears to cover what I need.
  27. I install the vmware client integration plugin from the ISO I downloaded previously on the windows VM (which is nearly out of space at this point).
  28. run upgrader from ISO.
  29. Walk through all the options and get to step 4 before getting the message: “vCenterServer FQDN does not match DNS servers “localhost.localdom,localhost” and ip addresses “” from VC certificate. Examine the VC certificate and make sure it is valid and point to vCenter Server FQDN.”
  30. Which if I’m reading correctly, means that before I can upgrade, I have to install a properly signed certificate on 5.5 for…. I’m gonna guess the :5480 interface, which may be a totally different cert than the one for :9443.
  31. research and find that I can circumvent this by setting the cert regeneration flag in the :5480 interface and rebooting vcenter.
  32. try the upgrade tool again
  33. realize 3 seconds after clicking OK that I just started a process of unknown length at 2pm on a friday.
  34. process finishes at 3pm, warns me that my license is about to expire for vcenter(!)
  35. re-enter license, am told it is no longer valid.
  36. panic as I realize our license is for vcenter 5, not vcenter 6.
  37. research and am told that it’s a simple upgrade procedure in the vmware portal to get a new license key for 6.0
  38. go through motions, import new key, everything is awesome.


I owe a tremendous debt of gratitude  towards the guys in #VMware on freenode. without their assistance, I’d probably be under my desk sobbing right now.


MONDAY: I’ll continue by upgrading the esx hosts. I’m sure it’ll go smoothly.


wacom Intuos tablet crashes Xorg in Kubuntu


every 1 in 10 times I hook up my Wacom Intuos to Kubuntu, xorg crashes with this lovely message. It’s very irksome.

Linux linwider 3.16.0-28-generic #38-Ubuntu SMP Fri Dec 12 17:37:40 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Kubuntu 14.10

xsetwacom –list devices
Wacom Intuos PT S Pen stylus id: 9 type: STYLUS
Wacom Intuos PT S Finger touch id: 10 type: TOUCH
Wacom Intuos PT S Pen eraser id: 16 type: ERASER
Wacom Intuos PT S Finger pad id: 17 type: PAD


Bus 001 Device 005: ID 056a:0302 Wacom Co., Ltd

[180969.708] (II) config/udev: Adding input device Wacom Intuos PT S Pen (/dev/input/mouse2)
[180969.708] (II) No input driver specified, ignoring this device.
[180969.708] (II) This device may have been added with another device file.
[180969.710] (II) config/udev: Adding input device Wacom Intuos PT S Finger (/dev/input/event16)
[180969.710] (**) Wacom Intuos PT S Finger: Applying InputClass "evdev touchpad catchall"
[180969.710] (**) Wacom Intuos PT S Finger: Applying InputClass "touchpad catchall"
[180969.710] (**) Wacom Intuos PT S Finger: Applying InputClass "Default clickpad buttons"
[180969.710] (**) Wacom Intuos PT S Finger: Applying InputClass "Wacom class"
[180969.710] (II) Using input driver 'wacom' for 'Wacom Intuos PT S Finger'
[180969.710] (**) Wacom Intuos PT S Finger: always reports core events
[180969.710] (**) Option "Device" "/dev/input/event16"
[180969.710] (EE) Wacom Intuos PT S Finger: Invalid type 'stylus' for this device.
[180969.710] (EE) Wacom Intuos PT S Finger: Invalid type 'eraser' for this device.
[180969.710] (EE) Wacom Intuos PT S Finger: Invalid type 'cursor' for this device.
[180969.710] (II) Wacom Intuos PT S Finger: type not specified, assuming 'touch'.
[180969.710] (II) Wacom Intuos PT S Finger: other types will be automatically added.
[180969.710] (--) Wacom Intuos PT S Finger touch: maxX=4096 maxY=4096 maxZ=0 resX=26000 resY=43000 
[180969.710] (II) Wacom Intuos PT S Finger touch: hotplugging dependent devices.
[180969.710] (EE) Wacom Intuos PT S Finger touch: Invalid type 'stylus' for this device.
[180969.710] (EE) Wacom Intuos PT S Finger touch: Invalid type 'eraser' for this device.
[180969.710] (EE) Wacom Intuos PT S Finger touch: Invalid type 'cursor' for this device.
[180969.710] (II) Wacom Intuos PT S Finger touch: hotplugging completed.
[180969.760] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1/input/input46/event16"
[180969.760] (II) XINPUT: Adding extended input device "Wacom Intuos PT S Finger touch" (type: TOUCH, id 14)
[180969.760] (**) Wacom Intuos PT S Finger touch: (accel) keeping acceleration scheme 1
[180969.760] (**) Wacom Intuos PT S Finger touch: (accel) acceleration profile 0
[180969.760] (**) Wacom Intuos PT S Finger touch: (accel) acceleration factor: 2.000
[180969.760] (**) Wacom Intuos PT S Finger touch: (accel) acceleration threshold: 4
[180969.760] (**) Wacom Intuos PT S Finger pad: Applying InputClass "evdev touchpad catchall"
[180969.761] (**) Wacom Intuos PT S Finger pad: Applying InputClass "touchpad catchall"
[180969.761] (**) Wacom Intuos PT S Finger pad: Applying InputClass "Default clickpad buttons"
[180969.761] (**) Wacom Intuos PT S Finger pad: Applying InputClass "Wacom class"
[180969.761] (II) Using input driver 'wacom' for 'Wacom Intuos PT S Finger pad'
[180969.761] (**) Wacom Intuos PT S Finger pad: always reports core events
[180969.761] (**) Option "Device" "/dev/input/event16"
[180969.761] (**) Option "Type" "pad"
[180969.772] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1/input/input46/event16"
[180969.772] (II) XINPUT: Adding extended input device "Wacom Intuos PT S Finger pad" (type: PAD, id 15)
[180969.772] (**) Wacom Intuos PT S Finger pad: (accel) keeping acceleration scheme 1
[180969.772] (**) Wacom Intuos PT S Finger pad: (accel) acceleration profile 0
[180969.772] (**) Wacom Intuos PT S Finger pad: (accel) acceleration factor: 2.000
[180969.772] (**) Wacom Intuos PT S Finger pad: (accel) acceleration threshold: 4
[180969.773] (II) config/udev: Adding input device Wacom Intuos PT S Pen (/dev/input/event15)
[180969.773] (**) Wacom Intuos PT S Pen: Applying InputClass "evdev tablet catchall"
[180969.773] (**) Wacom Intuos PT S Pen: Applying InputClass "Wacom class"
[180969.773] (II) Using input driver 'wacom' for 'Wacom Intuos PT S Pen'
[180969.773] (**) Wacom Intuos PT S Pen: always reports core events
[180969.773] (**) Option "Device" "/dev/input/event15"
[180969.773] (II) Wacom Intuos PT S Pen: type not specified, assuming 'stylus'.
[180969.773] (II) Wacom Intuos PT S Pen: other types will be automatically added.
[180969.773] (--) Wacom Intuos PT S Pen stylus: using pressure threshold of 27 for button 1
[180969.773] (--) Wacom Intuos PT S Pen stylus: maxX=15200 maxY=9500 maxZ=1023 resX=100000 resY=100000 tilt=enabled
[180969.773] (II) Wacom Intuos PT S Pen stylus: hotplugging dependent devices.
[180969.773] (EE) Wacom Intuos PT S Pen stylus: Invalid type 'cursor' for this device.
[180969.773] (EE) Wacom Intuos PT S Pen stylus: Invalid type 'touch' for this device.
[180969.773] (EE) Wacom Intuos PT S Pen stylus: Invalid type 'pad' for this device.
[180969.773] (II) Wacom Intuos PT S Pen stylus: hotplugging completed.
(EE) Backtrace:
(EE) 0: /usr/bin/X (xorg_backtrace+0x56) [0x7fb0bba1ce96]
(EE) 1: /usr/bin/X (0x7fb0bb866000+0x1bb099) [0x7fb0bba21099]
(EE) 2: /lib/x86_64-linux-gnu/ (0x7fb0b959a000+0x36eb0) [0x7fb0b95d0eb0]
(EE) 3: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0x103c0) [0x7fb0afee03c0]
(EE) 4: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0xe00d) [0x7fb0afede00d]
(EE) 5: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0x62cf) [0x7fb0afed62cf]
(EE) 6: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0x650e) [0x7fb0afed650e]
(EE) 7: /usr/bin/X (0x7fb0bb866000+0x95638) [0x7fb0bb8fb638]
(EE) 8: /usr/bin/X (0x7fb0bb866000+0xbfcc9) [0x7fb0bb925cc9]
(EE) 9: /lib/x86_64-linux-gnu/ (0x7fb0b959a000+0x36eb0) [0x7fb0b95d0eb0]
(EE) 10: /lib/x86_64-linux-gnu/ (close+0x2d) [0x7fb0b968679d]
(EE) 11: /usr/bin/X (xf86CloseSerial+0x21) [0x7fb0bb925521]
(EE) 12: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0x5c25) [0x7fb0afed5c25]
(EE) 13: /usr/lib/xorg/modules/input/ (0x7fb0afed0000+0x9c79) [0x7fb0afed9c79]
(EE) 14: /usr/bin/X (0x7fb0bb866000+0xa4948) [0x7fb0bb90a948]
(EE) 15: /usr/bin/X (0x7fb0bb866000+0xbb5b9) [0x7fb0bb9215b9]
(EE) 16: /usr/bin/X (0x7fb0bb866000+0xbb8f8) [0x7fb0bb9218f8]
(EE) 17: /usr/bin/X (WakeupHandler+0x6b) [0x7fb0bb8c1f1b]
(EE) 18: /usr/bin/X (WaitForSomething+0x1c7) [0x7fb0bba1a247]
(EE) 19: /usr/bin/X (0x7fb0bb866000+0x56fe1) [0x7fb0bb8bcfe1]
(EE) 20: /usr/bin/X (0x7fb0bb866000+0x5b3d6) [0x7fb0bb8c13d6]
(EE) 21: /lib/x86_64-linux-gnu/ (__libc_start_main+0xf5) [0x7fb0b95bbec5]
(EE) 22: /usr/bin/X (0x7fb0bb866000+0x4576e) [0x7fb0bb8ab76e]
(EE) Segmentation fault at address 0xa9a8
Fatal server error:
(EE) Caught signal 11 (Segmentation fault). Server aborting
Please consult the The X.Org Foundation support 
for help. 
(EE) Please also check the log file at "/var/log/Xorg.0.log" for additional information.
(II) AIGLX: Suspending AIGLX clients for VT switch
(EE) Server terminated with error (1). Closing log file.

Please Steal This Idea.


Someone take this idea and run with it; just make sure it’s free to use. I don’t have the time for it and it’s too great not to write down.


The idea? A simple graph paper map maker. Nothing with fancy graphics like campaign cartographer or FantasticMapper, Just a simple graph paper mapper.

The interface consists of two major parts- the map window and the collapsible sidebar.

Main Window

The main window looks like an unblemished minesweeper screen with a giant crosshair segmenting it into 4 parts. There is both a horizontal and vertical scrollbar. only 1/2 of the window is showing according to the scrollbars, meaning you can scroll left or right, up or down. In the bottom corner is a zoom tool similar to google maps.


The sidebar appears as a small button on the left that folds out when clicked. it contains a vertical accordion menu with the following headers:


[S]elect mode will let you click on an object underneath the cursor. multiple clicks will cycle focus on the next item in the stack underneath the cursor. This could be a Feature, Interior Wall, or Path.


[E]xcavate has two main options to toggle between- Excavate (default) and Fill. While these are excavated, clicking on squares in the main window will either be emptied or filled in. Using the shift key reverses the active option- Excavate becomes Fill and vice versa. Excavate is the default tool when the page is loaded.


[W]all would be relatively straight forward. it would only affect existing exterior walls, either in part, whole, or individual lengths. Options would include smooth, rough, and natural.

Interior Wall

[I]nterior Wall has several options and two modes. The primary mode “Snap” would snap the grid between two excavated squares; the secondary mode “Free” would allow straight lines to be drawn between two arbitrary points. Clicking and Dragging will draw out a temporary path until the mouse is released. Using the shift key lets you a rectangle of interior walls.  Options would include walls (default), half walls, engraved walls, magical forcefields, cliffs/ledges, ruined walls, lower level walls, walls with arrow slits, water lines, etc.


[D]oors would have several options, all of which snaps to and highlights a border between two squares. This would work between two dug squares or a dug and filled square (i.e. a fake/useless door). Options would include regular door (default), double door (2 squares long), secret door, portcullis, false door etc.


[F]eatures can be placed and resized on any map, and is layered between the floor tiles and the walls, allowing for things like puddles to be half-covered. This will contain a block of highlightable icons, which will let you draw an item that can be moved, resized, or spun. Icons include stairs (default), circular stairwell, debris, water, pit, pillar, altar, chair, throne, table, crate, barrel, fireplace, statue, well, sarcophagus, dias, bridge, carpet, etc.



[T]rap behavior would be dictated by type, but would mostly act like Features. Options would include pit traps (default), spike traps, blade traps, poison gas, etc.


[P]ath would allow you to draw a simple line from point A to point B. This could be a dispersed “sandy” type line, a dashed, dotted or solid line of configurable width.


[O]ptions would contain:

  • Line color (black, blue, gray)
  • Grid (none, excavated areas, all areas)
  • Grid Fade (100%, 50%, 25%)
  • Grid Color (black, blue, gray)
  • Border (click and drag region to be included in PNGs
  • Show Compass checkbox
  • Show scale checkbox
  • Tile Pattern (none, granite, stone, etc)
  • Fill Pattern (none, stone, line color, black)
  • square scale (5ft, 10ft, other)


[S]ave would give you the option of saving the output (SVG) to google drive, locally, or exporting to PDF if a border is not defined, it will do a best guess.


Right click would drag the map; +/- would zoom, arrow keys would pan. The map will pan infinitely in any direction, based off the centerpoint.

Hotkeys would include:

  • [S]elect mode
  • [E]xcavate
  • [I]nterior Wall
  • [D]oors
  • [F]eatures
  • [T]rap
  • [P]ath
  • [O]ptions
  • [S]ave
  • [ctrl+z] undo
  • [ctrl+shift+z] redo
  • standard copy/cut/paste

So that’s the idea that’s been kicking around in my head. If you’re a UI person and interested in helping me, I’d be glad to help give guidance on functionality, but I don’t have the time to develop it myself.

Geomorph Theory


*Random Crazy Person Thought of the Day: Ultra-specialized Geomorphs and Naming Conventions*

Standard Geomorph

A geomorph has 4 sides, and connects to all for sides via two entryways or “ports.” It looks a little bit like an octothorpe/hash with the center filled in (#).


Base2 Geomorph Sets

While Standard Geomorph tiles are cool, theres no way to close the system. To do this, you need to introduce the concept of a side being open (has two connecting ports) or closed (has no connecting ports).

Since there are only two options per side, we can represent each side with a binary number- 0 for closed, 1 for open. By using binary, we can now represent our tile as a four digit binary number. A four digit binary number has 16 possible states (0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, etc).  If we allow for rotation, we can reduce the total number of unique tiles needed, i.e. 1000, 0100, 0010 and 0001 can all be represented with the same one-sided geomorph by turning it.

With the addition of rotation, we can reduce our 16 down to 6 unique configurations:

0000= Sealed Geomorph
0001= One-sided Cave Geomorph
0011= Two-sided Corner Geomorph
0101= Two-sided Tunnel Geomorph
0111= Three-sided Edge Geomorph
1111= Four-sided Standard Geomorph

Layer #8Layer #4Layer #5 Layer #9Layer #7 Layer

Suppose we wanted to create store hundreds of tiles with these configurations. How would we store them? The most logical way is to create directories based on their configuration, which could be named after the binary number above. if you needed a eastern wall, you could translate it to 1011, which is simply a three-sided Edge geomorph with a 90 degree clockwise rotation. You could then snag a random one-sized edge tile from 1011/ and simply rotate it.

Base4 Geomorph Set

While this is neat, you can take it a step further with segmented geomorphs, which track the state of individual left and right ports:


00= both closed
01= first open
10= second open
11= both open

The addition of these two new states forces us to use 2 bits to represent state per side, or 8 bits total to represent a tile.


This means there are 256 different configurations for tiles. This can be reduced not only by rotation, but by flipping:

10 00 00 00 = Top left open
01 00 00 00 = Top right open (the above tile, flipped on it's Y axis)

(Also note, flipping along the X and Y axis has the same effect as rotating 180 degrees.)

00 10 00 00 = right top open
00 01 00 00 = right bottom open (flipped along X axis)
00 00 00 01 = left top open (flipped along Y axis)
00 00 00 10 = left bottom open (rotated 180 degrees)
00 00 00 10 = left bottom open (flipped along X and Y axis)


By the time you add in flipping and rotating, we end up with significantly less than 256 tiles. How much? I have no idea, the math is beyond me right now without drawing them all out. What I can say is that we can represent them with Base4 notation:

0= both closed
1= first open
2= second open
3= both open

Layer #8Layer #10 Layer #11Layer #4





This allows us to represent every tile category with only 4 digits. Looking at what we’ve represented previously:

0000= Sealed Geomorph
0003= One-sided Cave Geomorph
0033= Two-sided Corner Geomorph
0303= Two-sided Tunnel Geomorph
0333= Three-sided Edge Geomorph
3333= Four-sided Standard Geomorph

But we could also represent things like:

  • a pinwheel configuration: 1111
  • a crooked fork in the road:   0302
  • a narrow corridor  0102

PINWHEEL Layer #3 corridor

Among others.

Base8 Geomorphs

Lets take it another step- lets say that the solid center part between the two ports was changeable, essentially giving us 3 ports; three binary positions giving us a total of 8 combinations per side.

000 = all closed
001 = right open
010 = center open
011 = center and right open
100 = left open
101 = left and right open  (standard geomorph)
110 = center and left open
111 = all three open

With 3 bits per side, that gives us a total of 12 bits to represent a geomorph; If I remember my Base2 properly, that’s 4096 possible configurations (again much less with rotation and flips). We could still represent our standard configurations with only 4 digits if we use octal:

0000= Sealed Geomorph
0005= One-sided Cave Geomorph
0055= Two-sided corner Geomorph
0505= Two-sided Tunnel Geomorph
0555= Three-sided Edge Geomorph
5555= Four-sided Standard Geomorph

In addition we could create neat things like plusses, crosses, Y’s, trees, etc.

onesideelbowLayer #6 Layer #14 Layer #13

treeLayer #2 Layer #1 fatladder ladder

Base32 Geomorphs

If we wanted to take this one last insane step further, we could introduce the idea of ultra-specialized. where the 2 solid edges of each side were turned into ports. This means there are 5 binary areas (open or closed) per side, which translates to 32 configurations per side, meaning we can use base32 to encode each of the four sides with a simple four-letter code.

To this end, you could represent a “regular” geomorph side with the binary representation, i.e. 01010, which is 10 in decimal and A in base32. This means a regular geomorph tile would be encoded as AAAA.

0000=sealed Geomorph
A000= One-sided Geomorph
AA00= Two-sided Corner Geomorph
A0A0= Two-sided Tunnel Geomorph
AAA0= Three-sided Edge Geomorph
AAAA= Four-sided Standard Geomorph

So, the final tally? Five binary on 4 sides is 20 bits of data per tile; That’s over a million different variations. My brain hurts now.

Until I sat down and did the math, I thought 5bit-sided geomorphs were doable. Now I see how wrong I was.


The Key to Creating Good, Tileable Images (in GIMP)


I’m writing this as a general guide both for future reference, and to get feedback from others.

Often when using an image manipulation program such as GIMP or Photoshop, you’ll need to create large swaths of consistent texture. The easiest way to do this is with a pattern fill tool, however most programs only include a small set of patterns. The good news is that you can make your own with relatively little grief.

A quick note- While you may occasionally want an obvious tile (e.g. tiled floors), this discussion will focus on tiles that try to appear seamless.

Choose a Texture


For our example, I took a photo of the dirt outside. As you can see, it’s not perfectly uniform, but we’ll take care of that in a moment.

The first step is obviously to decide what you’d like to have as a texture- dirt, cement, gravel, treebark, marble, and leaves are all good examples of common textures. Your texture should be relatively consistent. While some variation is needed to give it flavor, it needs to be somewhat symmetric (i.e. a baseball in a tile of grass will make tiling obvious), however you may be able to cover that up.







Crop Inconsistencies


Here you can see I cropped out the large flat top right corner and dark bottom left corner. It’s starting to look a lot more consistent, however there’s still a few pesky details that stick out.

Sometimes your source picture will have an anomaly on one side, such as the edge of a sidewalk on a dirt texture, or stick in a field of grass that wasn’t quite out of frame. The simplest way to deal with this is to crop it out. Save as much as you can of the original image, but make sure to completely remove the inconsistency. Judicious cropping can also help you determine your focus- is your pattern a field of grass or blades of grass?









Remove Anomalies


Here you can see I removed quite a few wood chips, the twig, some tree buds, and a small sprout The consistency is coming along nicely at this point.

Should your texture has some type of anomaly (like the baseball mentioned above) that is too far in to safely crop, you can often use a combination of the rubber stamp tool and healing tool to copy a more generic spot over the anomaly and blend it into place.









Offset and Wrap


We were relatively lucky with the dirt; the seams are barely noticeable.

Now that our tile looks fairly consistent, lets examine the seams. For this we’ll need to offset the entire image by half (Layer->transform->offset or ctrl+shift+o) along the X and Y axis. This should give you a nice cross where the edges will meet in the final product. Quite often you will find color variation between the two sides- sometimes you can get lucky and dodge or burn the image to get them closer shades, sometimes it’s far more tricky (and beyond the scope of this document).


Using the four quadrants as a baseline, you can use the rubber stamp and healing tools to cover the seams- with any luck this process will be fairly simple and painless. Remember, the goal is to make the seams disappear, so be sure to feather it in unevenly, and not with a straight line that will still be visible.





This image should be seamlessly tileable at this point.

Once complete, we need to verify we didn’t accidentally damage leave any artifacts need the ends of the seams. To do this, do another offer, but only offset the Y by half; this may reveal a small horizontal seam near the center. Take care of that and perform a final offset, transition X by half. This should leave a small vertical seam. Once it’s resolved, you should have a nice, seamless texture… but we’re not done yet.









Unfortunately we do see some banding along the top and middle of the image; this could be corrected with some dodging and burning, but we’ll call it good for now.

Quadruple It

The phrase “can’t see the forest for the trees” applies here. We’ve seen what one intersection looks like- how about several? If we increase the image canvas size and duplicate time layer 3 more times, we can set them side-by-side and merge them down to identify redundant features that escaped us previously. Things you might see include:

  • That small twig may have been unnoticeable with one tile, but with many tiles it betrays the redundancy
  • There may have been an ever-so slight variation in color that was previously unnoticed
  • The area surrounding the original seams was not as well blended as previously thought
  • A small area that is simply too unique and sticks out just enough to be noticeable.

You have a choice at this point; you can either undo back to the single image, or choose to keep it quadrupled. If you keep it quadrupled, you can ad ever-so-slight modifications to each quadrant to help disperse the redundancy.


The final step is to export (File->export as… or ctrl+shift+e) and save the pattern as a .pat file.  This should be kept in your GIMP patterns folder (on linux ~/.gimp-2.8/patterns/)

The next time you refresh your patterns box, you should see your new texture.

Holy Cow it’s too Big!

Oh, all that work we just put in? It may be worthless; I shoulda mentioned that up front.

Here’s the problem: If your base texture is 2500×2000 pixels, don’t be surprised that your pattern is gigantic when you try to use it. As of right now, GIMP doesn’t have a built-in way to scale patterns (although there are plugins that claim to do it). Your best bet is to scale the image down before exporting it to a pat file, just be warned that the scaled image may have seams reappear from the scaling interpolation, so you may need to run through the offsets again to verify that it’s acceptable.

Final Note…

  • No, I cannot tell you how to do this in photoshop.
  • I despise capitalizing GIMP. it I understand why it’s supposed to be, but it still feels super lame.
  • If you have feedback, please leave a comment below- I’d love to improve my process.
  • If you liked this tutorial, please consider supporting me via Patreon

Fix for Sonar Breaking After Upgrade


Upgrade is not supported. Please use a production-ready database.


If you’ve ever seen this message after a yum update, you know how infuriating it can be. I was sure I was already using mysql rather than the default h2 database, but everything indicated that was the problem.


It turns out the error was caused when they replaced /opt/sonar/conf/ with a default configuration. If you vimdiff it against /opt/sonar/conf/ you should see the issue.


Let me know if this helps.

Prunecluster’s Stories, Volume 1, Part 1: Cragmaw Cave.


So there we were… Cragmaw Cave. We’d just killed a bunch of goblins and had located their leader, known only as “the big one.” The ranger peeks into the small cavern to appraise the situation, then returns to us. “There’s a Bugbear, a worg, and… a goblin in a jester outfit.”

“Whut whut?” I ask. I take my craft seriously. There’s no way the goblin is a properly trained jester. At best he does some pratfalls and slapstick. He ain’t no artist. I cannot let this travesty stand. I fly into the room alone with a series of cartwheels, leaving my fellow travelers dumbstruck. I demonstrated some dance moves and ended with a challenge.

“I CHALLENGE YOU… TO A JEST-OFF!” I spat at the ugly like hack. The worg starts to get up, but the bugbear stops him, enthralled by my performance, “Proceed,” he tells the goblin.

I bust out my iron golem dance while the goblin goes for the snake. *Pssht*, the snake. Anyone can do the snake- hell, my grandma taught me the snake. Figures that a stupid bugbear would like it better. Realising the brute only valued physical prowess and not nuance, I decided to display my acrobatic skills.  The bugbear was awestruck by my ending flourish.

“You’re fired,” the Bugbear said to the goblin, motioning to the worg, who leapt on to the goblin and ripped out his throat.

“You ready for my next trick?” I asked, and began weaving an intricate version of the Dwarven Aristocrats, with just a tinge of magic. Had it worked, he’d have been doubled over on the floor, laughing uncontrollably… unfortunately, he found dwarven culture too refined for his tastes. The bugbear grew violent, at which point I beat a hasty retreat out of the room with bugbear and worg hot on my heels. Boy were they surprised when they found my friends waiting for them around the corner…


If there’s one thing I hate more than an hack jester, it’s an ungrateful crowd.

Go to Top