This commit is contained in:
James Page 2015-09-09 09:32:45 +01:00
parent 7c6a5509b0
commit 023fa61aea
6 changed files with 33 additions and 311 deletions

1
.bzrignore Normal file
View File

@ -0,0 +1 @@
bin

View File

@ -2,7 +2,7 @@
PYTHON := /usr/bin/env python
lint:
@flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
@flake8 --exclude hooks/charmhelpers hooks
@charm proof
unit_test:

View File

@ -1,253 +0,0 @@
#!/usr/bin/python
# Copyright 2014-2015 Canonical Limited.
#
# This file is part of charm-helpers.
#
# charm-helpers is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3 as
# published by the Free Software Foundation.
#
# charm-helpers is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
# Authors:
# Adam Gandelman <adamg@ubuntu.com>
import logging
import optparse
import os
import subprocess
import shutil
import sys
import tempfile
import yaml
from fnmatch import fnmatch
import six
CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
def parse_config(conf_file):
if not os.path.isfile(conf_file):
logging.error('Invalid config file: %s.' % conf_file)
return False
return yaml.load(open(conf_file).read())
def clone_helpers(work_dir, branch):
dest = os.path.join(work_dir, 'charm-helpers')
logging.info('Checking out %s to %s.' % (branch, dest))
cmd = ['bzr', 'checkout', '--lightweight', branch, dest]
subprocess.check_call(cmd)
return dest
def _module_path(module):
return os.path.join(*module.split('.'))
def _src_path(src, module):
return os.path.join(src, 'charmhelpers', _module_path(module))
def _dest_path(dest, module):
return os.path.join(dest, _module_path(module))
def _is_pyfile(path):
return os.path.isfile(path + '.py')
def ensure_init(path):
'''
ensure directories leading up to path are importable, omitting
parent directory, eg path='/hooks/helpers/foo'/:
hooks/
hooks/helpers/__init__.py
hooks/helpers/foo/__init__.py
'''
for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):
_i = os.path.join(d, '__init__.py')
if not os.path.exists(_i):
logging.info('Adding missing __init__.py: %s' % _i)
open(_i, 'wb').close()
def sync_pyfile(src, dest):
src = src + '.py'
src_dir = os.path.dirname(src)
logging.info('Syncing pyfile: %s -> %s.' % (src, dest))
if not os.path.exists(dest):
os.makedirs(dest)
shutil.copy(src, dest)
if os.path.isfile(os.path.join(src_dir, '__init__.py')):
shutil.copy(os.path.join(src_dir, '__init__.py'),
dest)
ensure_init(dest)
def get_filter(opts=None):
opts = opts or []
if 'inc=*' in opts:
# do not filter any files, include everything
return None
def _filter(dir, ls):
incs = [opt.split('=').pop() for opt in opts if 'inc=' in opt]
_filter = []
for f in ls:
_f = os.path.join(dir, f)
if not os.path.isdir(_f) and not _f.endswith('.py') and incs:
if True not in [fnmatch(_f, inc) for inc in incs]:
logging.debug('Not syncing %s, does not match include '
'filters (%s)' % (_f, incs))
_filter.append(f)
else:
logging.debug('Including file, which matches include '
'filters (%s): %s' % (incs, _f))
elif (os.path.isfile(_f) and not _f.endswith('.py')):
logging.debug('Not syncing file: %s' % f)
_filter.append(f)
elif (os.path.isdir(_f) and not
os.path.isfile(os.path.join(_f, '__init__.py'))):
logging.debug('Not syncing directory: %s' % f)
_filter.append(f)
return _filter
return _filter
def sync_directory(src, dest, opts=None):
if os.path.exists(dest):
logging.debug('Removing existing directory: %s' % dest)
shutil.rmtree(dest)
logging.info('Syncing directory: %s -> %s.' % (src, dest))
shutil.copytree(src, dest, ignore=get_filter(opts))
ensure_init(dest)
def sync(src, dest, module, opts=None):
# Sync charmhelpers/__init__.py for bootstrap code.
sync_pyfile(_src_path(src, '__init__'), dest)
# Sync other __init__.py files in the path leading to module.
m = []
steps = module.split('.')[:-1]
while steps:
m.append(steps.pop(0))
init = '.'.join(m + ['__init__'])
sync_pyfile(_src_path(src, init),
os.path.dirname(_dest_path(dest, init)))
# Sync the module, or maybe a .py file.
if os.path.isdir(_src_path(src, module)):
sync_directory(_src_path(src, module), _dest_path(dest, module), opts)
elif _is_pyfile(_src_path(src, module)):
sync_pyfile(_src_path(src, module),
os.path.dirname(_dest_path(dest, module)))
else:
logging.warn('Could not sync: %s. Neither a pyfile or directory, '
'does it even exist?' % module)
def parse_sync_options(options):
if not options:
return []
return options.split(',')
def extract_options(inc, global_options=None):
global_options = global_options or []
if global_options and isinstance(global_options, six.string_types):
global_options = [global_options]
if '|' not in inc:
return (inc, global_options)
inc, opts = inc.split('|')
return (inc, parse_sync_options(opts) + global_options)
def sync_helpers(include, src, dest, options=None):
if not os.path.isdir(dest):
os.makedirs(dest)
global_options = parse_sync_options(options)
for inc in include:
if isinstance(inc, str):
inc, opts = extract_options(inc, global_options)
sync(src, dest, inc, opts)
elif isinstance(inc, dict):
# could also do nested dicts here.
for k, v in six.iteritems(inc):
if isinstance(v, list):
for m in v:
inc, opts = extract_options(m, global_options)
sync(src, dest, '%s.%s' % (k, inc), opts)
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-c', '--config', action='store', dest='config',
default=None, help='helper config file')
parser.add_option('-D', '--debug', action='store_true', dest='debug',
default=False, help='debug')
parser.add_option('-b', '--branch', action='store', dest='branch',
help='charm-helpers bzr branch (overrides config)')
parser.add_option('-d', '--destination', action='store', dest='dest_dir',
help='sync destination dir (overrides config)')
(opts, args) = parser.parse_args()
if opts.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
if opts.config:
logging.info('Loading charm helper config from %s.' % opts.config)
config = parse_config(opts.config)
if not config:
logging.error('Could not parse config from %s.' % opts.config)
sys.exit(1)
else:
config = {}
if 'branch' not in config:
config['branch'] = CHARM_HELPERS_BRANCH
if opts.branch:
config['branch'] = opts.branch
if opts.dest_dir:
config['destination'] = opts.dest_dir
if 'destination' not in config:
logging.error('No destination dir. specified as option or config.')
sys.exit(1)
if 'include' not in config:
if not args:
logging.error('No modules to sync specified as option or config.')
sys.exit(1)
config['include'] = []
[config['include'].append(a) for a in args]
sync_options = None
if 'options' in config:
sync_options = config['options']
tmpd = tempfile.mkdtemp()
try:
checkout = clone_helpers(tmpd, config['branch'])
sync_helpers(config['include'], checkout, config['destination'],
options=sync_options)
except Exception as e:
logging.error("Could not sync: %s" % e)
raise e
finally:
logging.debug('Cleaning up %s' % tmpd)
shutil.rmtree(tmpd)

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
from socket import gethostname
from subprocess import check_call
import sys
from charmhelpers.core.hookenv import (
@ -11,17 +10,8 @@ from charmhelpers.core.hookenv import (
log,
unit_get,
relation_set,
relation_ids,
)
from charmhelpers.core.host import (
adduser,
mkdir,
restart_on_change,
service_start
)
from lxd_utils import (
install_lxd,
)
@ -34,6 +24,7 @@ def install():
log('Instatlling LXD')
install_lxd()
@hooks.hook('lxd-relation-joined')
def relation_joined(rid=None):
settings = {}
@ -45,11 +36,13 @@ def relation_joined(rid=None):
relation_set(relation_id=rid,
relation_settings=settings)
def main():
try:
hooks.execute(sys.argv)
except UnregisteredHookError as e:
log("Unknown hook {} - skipping.".format(e))
if __name__ == "__main__":
main()

View File

@ -1,15 +1,11 @@
import glob
import pwd
import getpass
import re
import os
import sys
import shutil
from subprocess import call, check_call, check_output, CalledProcessError
from subprocess import call, check_call
from charmhelpers.core.templating import render
from charmhelpers.core.decorators import retry_on_exception
from charmhelpers.core.hookenv import (
log,
@ -18,45 +14,33 @@ from charmhelpers.core.hookenv import (
)
from charmhelpers.core.host import (
adduser,
add_group,
add_user_to_group,
mkdir,
service_restart,
service_stop,
lsb_release,
write_file,
mount, umount,
mount,
)
from charmhelpers.contrib.openstack.utils import (
configure_installation_source
)
from charmhelpers.payload.execd import execd_preinstall
from charmhelpers.contrib.storage.linux.utils import (
is_block_device,
)
from charmhelpers.contrib.storage.linux.loopback import (
ensure_loopback_device,
)
from charmhelpers.contrib.storage.linux.loopback import (
ensure_loopback_device
)
from charmhelpers.fetch import (
apt_upgrade,
apt_update,
apt_install,
add_source,
install_remote,
from charmhelpers.contrib.storage.linux.lvm import (
create_lvm_volume_group,
create_lvm_physical_volume
)
from charmhelpers.contrib.python.packages import (
pip_install,
from charmhelpers.fetch import (
apt_update,
apt_install,
)
BASE_PACKAGES = ['btrfs-tools', 'lvm2']
@ -73,16 +57,18 @@ LXD_SOURCE_PACKAGES = [
'golang',
'xz-utils',
'tar',
'acl']
'acl',
]
LXD_GIT = 'github.com/lxc/lxd'
DEFAULT_LOOPBACK_SIZE = '10G'
def install_lxd():
execd_preinstall()
lxd_src = config('lxd-origin')
configure_installation_source(lxd_src)
apt_update()
configure_installation_source(config('lxd-origin'))
apt_update(fatal=True)
apt_install(determine_packages(), fatal=True)
if config('lxd-use-source'):
install_lxd_source()
configure_lxd_source()
@ -90,17 +76,15 @@ def install_lxd():
service_stop('lxd')
configure_lxd_block()
configure_lxd_host()
service_restart('lxd')
def install_lxd_source(user='ubuntu'):
log('Intsalling LXD Source')
log('Installing LXD Source')
home = pwd.getpwnam(user).pw_dir
GOPATH = os.path.join(home, 'go')
LXD_SRC = os.path.join(GOPATH, 'src', 'github.com/lxc/lxd')
LXD_BIN = os.path.join(GOPATH, 'bin', 'lxd')
if not os.path.exists(GOPATH):
mkdir(GOPATH)
@ -109,7 +93,7 @@ def install_lxd_source(user='ubuntu'):
env['GOPATH'] = GOPATH
env['HTTP_PROXY'] = 'http://squid.internal:3128'
env['HTTPS_PROXY'] = 'https://squid.internal:3128'
cmd = 'go get -v github.com/lxc/lxd'
cmd = 'go get -v %s' % LXD_GIT
log('Installing LXD: %s' % (cmd))
check_call(cmd, env=env, shell=True)
@ -148,18 +132,12 @@ def configure_lxd_source(user='ubuntu'):
add_group('lxd', system_group=True)
add_user_to_group(user, 'lxd')
dest_dir = '/usr/bin'
files = glob.glob('%s/bin/*' % GOPATH)
for i in files:
cmd = ['cp', i, '/usr/bin']
check_call(cmd)
def configure_lxd_host():
log('Configuring LXD host')
service_restart('lxd')
def configure_lxd_block():
log('Configuring LXD block device')
lxd_block_device = config('lxd-block-device')
@ -180,9 +158,11 @@ def configure_lxd_block():
persist=True,
filesystem='btrfs')
elif config('lxd-fs-type') == 'lvm':
for dev in determine_block_devices():
cmd = ['vgcreate', 'lxd_vg', dev]
check_call(cmd)
devices = determine_block_devices()
if devices:
for dev in devices:
create_lvm_physical_volume(dev)
create_lvm_volume_group('lxd_vg', dev)
def find_block_devices():

View File

@ -3,8 +3,9 @@ summary: A lightervisor for LXC containers
maintainer: Chuck Short <zulcss@ubuntu.com>
description: |
A lightervisor for LXC containers
categories:
tags:
- misc
- openstack
subordinate: true
provides:
lxd: