summaryrefslogtreecommitdiffstats
path: root/puppethost.py
diff options
context:
space:
mode:
authorTodd Zullinger <tmz@pobox.com>2008-11-25 11:02:33 -0500
committerTodd Zullinger <tmz@pobox.com>2008-11-25 17:39:29 -0500
commite1de241f356a87ff8ca99aafba99f5bbcd5d5323 (patch)
tree56ea677b71adb735eb5cb54a53a66338a7a9464e /puppethost.py
downloadpuppet-host-package-e1de241f356a87ff8ca99aafba99f5bbcd5d5323.tar.gz
puppet-host-package-e1de241f356a87ff8ca99aafba99f5bbcd5d5323.tar.xz
puppet-host-package-e1de241f356a87ff8ca99aafba99f5bbcd5d5323.zip
Initial commit for puppet host package tool
Diffstat (limited to 'puppethost.py')
-rw-r--r--puppethost.py200
1 files changed, 200 insertions, 0 deletions
diff --git a/puppethost.py b/puppethost.py
new file mode 100644
index 0000000..46be2c0
--- /dev/null
+++ b/puppethost.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+"""Create a host package for bootstrapping a puppet client."""
+
+import os
+import glob
+import time
+import shutil
+import OpenSSL
+import tarfile
+import commands
+import optparse
+import tempfile
+
+ssldir = '/etc/puppet/ssl'
+defaults = {
+ 'destssldir': ssldir,
+ 'domain': '',
+ 'force': False,
+ 'force_cert': False,
+ 'force_tarball': False,
+ 'force_package': False,
+ 'release': '1',
+ 'rpmdir': os.environ['HOME'],
+ 'ssldir': ssldir,
+ 'template': '%s/template.spec' % ssldir,
+}
+
+status, domain = commands.getstatusoutput('dnsdomainname')
+if status == 0:
+ defaults['domain'] = domain
+
+package_types = ['deb', 'rpm']
+
+class PuppetHostError(StandardError):
+ pass
+
+class PuppetHost(object):
+ def __init__(self, hostname = '', opts = defaults):
+ """Puppet client class"""
+ if not hostname:
+ raise PuppetHostError('A hostname must be provided')
+
+ if '.' not in hostname and domain:
+ hostname = '.'.join([hostname, domain])
+ hostname = hostname.lower()
+
+ self.hostname = hostname
+ self.opts = opts
+ self.debfile = ''
+ self.rpmfile = ''
+ self.tarfile = ''
+ self._version = ''
+
+ def gencert(self):
+ """Generate a puppet certificate"""
+ cert = self.files['cert']
+ ssldir = self.opts['ssldir']
+ if os.path.exists(cert) and not self.opts['force_cert']:
+ raise PuppetHostError('%s exists, not overwriting' % cert)
+ cmd = 'puppetca --generate --ssldir %s %s' % (ssldir, self.hostname)
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ raise PuppetHostError('Error generating cert for %s: %s' %
+ (self.hostname, output))
+
+ def package(self, types = ['rpm']):
+ """Create packages in requested formats"""
+ for type in types:
+ if type not in package_types or not hasattr(self, type):
+ raise PuppetHostError('Bogus package type: %s' % type)
+ try: getattr(self, type)()
+ except Exception, error:
+ print error
+ continue
+
+ def deb(self):
+ """Create a .deb package"""
+ raise NotImplementedError('FIXME if you need debian packages.')
+
+ def tar(self, dir = '', keep = False):
+ self._check_files()
+
+ if not dir:
+ dir = tempfile.mkdtemp('', 'puppet-client-')
+
+ name = 'puppet-%s-%s' % (self.hostname, self.version)
+ tarball = '%s/%s.tar.gz' % (dir, name)
+
+ if os.path.exists(tarball) and not self.opts['force_tarball']:
+ raise PuppetHostError('%s exists, not overwriting' % tarball)
+
+ tar = tarfile.open(tarball, 'w:gz')
+ tar.dereference = True
+ for f in sorted(self.files.values()):
+ arcname = '%s%s' % (name, f.replace(self.opts['ssldir'], ''))
+ tar.add(f, arcname)
+ tar.close()
+ self.tarfile = tarball
+
+ def rpm(self):
+ """Create a .rpm package"""
+ rpmdir = self.opts['rpmdir']
+ tmpdir = tempfile.mkdtemp('', 'puppet-client-')
+
+ if not self.tarfile or not os.path.exists(self.tarfile):
+ try: self.tar(tmpdir)
+ except:
+ self._cleanup(tmpdir)
+ raise
+
+ specfile = '%s/puppet-%s.spec' % (tmpdir, self.hostname)
+
+ text = open(self.opts['template']).read()
+ text = text.replace('__HOSTNAME__', self.hostname)
+ text = text.replace('__VERSION__', self.version)
+ text = text.replace('__RELEASE__', self.opts['release'])
+ text = text.replace('__SSLDIR__', self.opts['destssldir'])
+
+ spec = open(specfile, 'w')
+ spec.write(text)
+ spec.close()
+
+ build_name_fmt = '%{N}-%{V}-%{R}.%{ARCH}.rpm'
+ rpmfilecmd = 'rpm -q --qf "%s/%s" --specfile %s' % (
+ rpmdir, build_name_fmt, specfile)
+ status, rpm = commands.getstatusoutput(rpmfilecmd)
+ if status:
+ self._cleanup(tmpdir)
+ raise PuppetHostError('Failed to get rpm filename:\n%s' % rpm)
+
+ if os.path.exists(rpm) and not self.opts['force_package']:
+ self._cleanup(tmpdir)
+ raise PuppetHostError('%s exists, not overwriting' % rpm)
+
+ rpmbuild = 'rpmbuild -bb'
+ rpmbuild += ' --define "_build_name_fmt %s"' % build_name_fmt
+ for i in ['_builddir', '_sourcedir', '_specdir', '_srcrpmdir']:
+ rpmbuild += ' --define "%s %s"' % (i, tmpdir)
+ rpmbuild += ' --define "_rpmdir %s" %s' % (rpmdir, specfile)
+
+ status, output = commands.getstatusoutput(rpmbuild)
+ if status or not os.path.exists(rpm):
+ self._cleanup(tmpdir)
+ raise PuppetHostError('Error building rpm:\n', output)
+
+ self._cleanup(tmpdir)
+ self.rpmfile = rpm
+
+ def _check_files(self):
+ """Check for the files we care about"""
+ self.files
+ self.missing = [f for f in self.files.values()
+ if not os.path.exists(f)]
+ if self.missing:
+ error = 'The following required files are missing:\n'
+ for f in self.missing:
+ error += '\t%s' % f
+ raise PuppetHostError(error)
+
+ def _cleanup(self, dir):
+ if os.path.isdir(dir):
+ shutil.rmtree(dir)
+
+ @property
+ def files(self):
+ hostname = self.hostname
+ ssldir = self.opts['ssldir']
+ return {
+ 'crl': '%s/ca/ca_crl.pem' % ssldir,
+ 'cacert': '%s/certs/ca.pem' % ssldir,
+ 'cert': '%s/certs/%s.pem' % (ssldir, hostname),
+ 'private': '%s/private_keys/%s.pem' % (ssldir, hostname),
+ }
+
+ @property
+ def version(self):
+ """Get the date from a certificate"""
+ if self._version:
+ return self._version
+
+ self._check_files()
+ cert = self.files['cert']
+
+ try:
+ x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ open(cert).read())
+ self._version = x509.get_notBefore()[:8]
+ except:
+ # pyOpenSSL < 0.7 lacks load_certificate()... do it the hard way
+ format = '%Y%m%d'
+ cmd = 'openssl x509 -in %s -text -noout' % cert
+ status, output = commands.getstatusoutput(cmd)
+ if status == 0:
+ date = [l.split(':', 1)[1].lstrip() for l in output.split('\n')
+ if 'Not Before:' in l][0]
+ fmt = '%b %d %H:%M:%S %Y %Z'
+ self._version = time.strftime(format, time.strptime(date, fmt))
+ else:
+ self._version = time.strftime(format)
+ return self._version