+++ /dev/null
-#!/usr/bin/make
-
-all: lint unit_test
-
-
-.PHONY: clean
-clean:
- @rm -rf .tox
-
-.PHONY: apt_prereqs
-apt_prereqs:
- @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
- @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
-
-.PHONY: lint
-lint: apt_prereqs
- @tox --notest
- @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
- @which charm > /dev/null || (sudo apt-get install -y charm)
- @charm proof
-
-.PHONY: unit_test
-unit_test: apt_prereqs
- @echo Starting tests...
- tox
+++ /dev/null
-#!/usr/bin/env python3
-
-import sys
-sys.path.append('lib')
-
-import argparse
-from charms.layer import options
-
-
-parser = argparse.ArgumentParser(description='Access layer options.')
-parser.add_argument('section',
- help='the section, or layer, the option is from')
-parser.add_argument('option',
- help='the option to access')
-
-args = parser.parse_args()
-value = options(args.section).get(args.option, '')
-if isinstance(value, bool):
- sys.exit(0 if value else 1)
-elif isinstance(value, list):
- for val in value:
- print(val)
-else:
- print(value)
options:
vpe-router:
- default: !!null ""
+ default:
type: string
description: Hostname or IP of the vpe router to connect to
user:
description: Username for VPE Router
pass:
type: string
- default: !!null ""
+ default:
description: Password for VPE Router
hostname:
type: string
- default: !!null ""
+ default:
description: The hostname to set the vpe router to.
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import os
-import sys
-sys.path.append('lib')
-
-# This is an upgrade-charm context, make sure we install latest deps
-if not os.path.exists('wheelhouse/.upgrade'):
- open('wheelhouse/.upgrade', 'w').close()
- if os.path.exists('wheelhouse/.bootstrapped'):
- os.unlink('wheelhouse/.bootstrapped')
-else:
- os.unlink('wheelhouse/.upgrade')
-
-from charms.layer import basic
-basic.bootstrap_charm_deps()
-basic.init_config_states()
-
-
-# This will load and run the appropriate @hook and other decorated
-# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
-# and $CHARM_DIR/hooks/relations.
-#
-# See https://jujucharms.com/docs/stable/authors-charm-building
-# for more information on this pattern.
-from charms.reactive import main
-main()
-"options":
- "basic":
- "packages":
- - "python-dev"
- - "libffi-dev"
- - "libssl-dev"
- "use_venv": !!bool "false"
- "include_system_packages": !!bool "false"
- "vpe-router": {}
-"includes":
-- "layer:basic"
-"is": "vpe-router"
+includes: ['layer:basic']
+++ /dev/null
-import os
-
-
-class LayerOptions(dict):
- def __init__(self, layer_file, section=None):
- import yaml # defer, might not be available until bootstrap
- with open(layer_file) as f:
- layer = yaml.safe_load(f.read())
- opts = layer.get('options', {})
- if section and section in opts:
- super(LayerOptions, self).__init__(opts.get(section))
- else:
- super(LayerOptions, self).__init__(opts)
-
-
-def options(section=None, layer_file=None):
- if not layer_file:
- base_dir = os.environ.get('CHARM_DIR', os.getcwd())
- layer_file = os.path.join(base_dir, 'layer.yaml')
-
- return LayerOptions(layer_file, section)
+++ /dev/null
-import os
-import sys
-import shutil
-import platform
-from glob import glob
-from subprocess import check_call
-
-from charms.layer.execd import execd_preinstall
-
-
-def bootstrap_charm_deps():
- """
- Set up the base charm dependencies so that the reactive system can run.
- """
- # execd must happen first, before any attempt to install packages or
- # access the network, because sites use this hook to do bespoke
- # configuration and install secrets so the rest of this bootstrap
- # and the charm itself can actually succeed. This call does nothing
- # unless the operator has created and populated $CHARM_DIR/exec.d.
- execd_preinstall()
- # ensure that $CHARM_DIR/bin is on the path, for helper scripts
- os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin')
- venv = os.path.abspath('../.venv')
- vbin = os.path.join(venv, 'bin')
- vpip = os.path.join(vbin, 'pip')
- vpy = os.path.join(vbin, 'python')
- if os.path.exists('wheelhouse/.bootstrapped'):
- from charms import layer
- cfg = layer.options('basic')
- if cfg.get('use_venv') and '.venv' not in sys.executable:
- # activate the venv
- os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
- reload_interpreter(vpy)
- return
- # bootstrap wheelhouse
- if os.path.exists('wheelhouse'):
- with open('/root/.pydistutils.cfg', 'w') as fp:
- # make sure that easy_install also only uses the wheelhouse
- # (see https://github.com/pypa/pip/issues/410)
- charm_dir = os.environ['CHARM_DIR']
- fp.writelines([
- "[easy_install]\n",
- "allow_hosts = ''\n",
- "find_links = file://{}/wheelhouse/\n".format(charm_dir),
- ])
- apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml'])
- from charms import layer
- cfg = layer.options('basic')
- # include packages defined in layer.yaml
- apt_install(cfg.get('packages', []))
- # if we're using a venv, set it up
- if cfg.get('use_venv'):
- if not os.path.exists(venv):
- distname, version, series = platform.linux_distribution()
- if series in ('precise', 'trusty'):
- apt_install(['python-virtualenv'])
- else:
- apt_install(['virtualenv'])
- cmd = ['virtualenv', '-ppython3', '--never-download', venv]
- if cfg.get('include_system_packages'):
- cmd.append('--system-site-packages')
- check_call(cmd)
- os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
- pip = vpip
- else:
- pip = 'pip3'
- # save a copy of system pip to prevent `pip3 install -U pip`
- # from changing it
- if os.path.exists('/usr/bin/pip'):
- shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
- # need newer pip, to fix spurious Double Requirement error:
- # https://github.com/pypa/pip/issues/56
- check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
- 'pip'])
- # install the rest of the wheelhouse deps
- check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] +
- glob('wheelhouse/*'))
- if not cfg.get('use_venv'):
- # restore system pip to prevent `pip3 install -U pip`
- # from changing it
- if os.path.exists('/usr/bin/pip.save'):
- shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
- os.remove('/usr/bin/pip.save')
- os.remove('/root/.pydistutils.cfg')
- # flag us as having already bootstrapped so we don't do it again
- open('wheelhouse/.bootstrapped', 'w').close()
- # Ensure that the newly bootstrapped libs are available.
- # Note: this only seems to be an issue with namespace packages.
- # Non-namespace-package libs (e.g., charmhelpers) are available
- # without having to reload the interpreter. :/
- reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
-
-
-def reload_interpreter(python):
- """
- Reload the python interpreter to ensure that all deps are available.
-
- Newly installed modules in namespace packages sometimes seemt to
- not be picked up by Python 3.
- """
- os.execle(python, python, sys.argv[0], os.environ)
-
-
-def apt_install(packages):
- """
- Install apt packages.
-
- This ensures a consistent set of options that are often missed but
- should really be set.
- """
- if isinstance(packages, (str, bytes)):
- packages = [packages]
-
- env = os.environ.copy()
-
- if 'DEBIAN_FRONTEND' not in env:
- env['DEBIAN_FRONTEND'] = 'noninteractive'
-
- cmd = ['apt-get',
- '--option=Dpkg::Options::=--force-confold',
- '--assume-yes',
- 'install']
- check_call(cmd + packages, env=env)
-
-
-def init_config_states():
- import yaml
- from charmhelpers.core import hookenv
- from charms.reactive import set_state
- from charms.reactive import toggle_state
- config = hookenv.config()
- config_defaults = {}
- config_defs = {}
- config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
- if os.path.exists(config_yaml):
- with open(config_yaml) as fp:
- config_defs = yaml.load(fp).get('options', {})
- config_defaults = {key: value.get('default')
- for key, value in config_defs.items()}
- for opt in config_defs.keys():
- if config.changed(opt):
- set_state('config.changed')
- set_state('config.changed.{}'.format(opt))
- toggle_state('config.set.{}'.format(opt), config.get(opt))
- toggle_state('config.default.{}'.format(opt),
- config.get(opt) == config_defaults[opt])
- hookenv.atexit(clear_config_states)
-
-
-def clear_config_states():
- from charmhelpers.core import hookenv, unitdata
- from charms.reactive import remove_state
- config = hookenv.config()
- remove_state('config.changed')
- for opt in config.keys():
- remove_state('config.changed.{}'.format(opt))
- remove_state('config.set.{}'.format(opt))
- remove_state('config.default.{}'.format(opt))
- unitdata.kv().flush()
+++ /dev/null
-# Copyright 2014-2016 Canonical Limited.
-#
-# This file is part of layer-basic, the reactive base layer for Juju.
-#
-# 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/>.
-
-# This module may only import from the Python standard library.
-import os
-import sys
-import subprocess
-import time
-
-'''
-execd/preinstall
-
-It is often necessary to configure and reconfigure machines
-after provisioning, but before attempting to run the charm.
-Common examples are specialized network configuration, enabling
-of custom hardware, non-standard disk partitioning and filesystems,
-adding secrets and keys required for using a secured network.
-
-The reactive framework's base layer invokes this mechanism as
-early as possible, before any network access is made or dependencies
-unpacked or non-standard modules imported (including the charms.reactive
-framework itself).
-
-Operators needing to use this functionality may branch a charm and
-create an exec.d directory in it. The exec.d directory in turn contains
-one or more subdirectories, each of which contains an executable called
-charm-pre-install and any other required resources. The charm-pre-install
-executables are run, and if successful, state saved so they will not be
-run again.
-
- $CHARM_DIR/exec.d/mynamespace/charm-pre-install
-
-An alternative to branching a charm is to compose a new charm that contains
-the exec.d directory, using the original charm as a layer,
-
-A charm author could also abuse this mechanism to modify the charm
-environment in unusual ways, but for most purposes it is saner to use
-charmhelpers.core.hookenv.atstart().
-'''
-
-
-def default_execd_dir():
- return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
-
-
-def execd_module_paths(execd_dir=None):
- """Generate a list of full paths to modules within execd_dir."""
- if not execd_dir:
- execd_dir = default_execd_dir()
-
- if not os.path.exists(execd_dir):
- return
-
- for subpath in os.listdir(execd_dir):
- module = os.path.join(execd_dir, subpath)
- if os.path.isdir(module):
- yield module
-
-
-def execd_submodule_paths(command, execd_dir=None):
- """Generate a list of full paths to the specified command within exec_dir.
- """
- for module_path in execd_module_paths(execd_dir):
- path = os.path.join(module_path, command)
- if os.access(path, os.X_OK) and os.path.isfile(path):
- yield path
-
-
-def execd_sentinel_path(submodule_path):
- module_path = os.path.dirname(submodule_path)
- execd_path = os.path.dirname(module_path)
- module_name = os.path.basename(module_path)
- submodule_name = os.path.basename(submodule_path)
- return os.path.join(execd_path,
- '.{}_{}.done'.format(module_name, submodule_name))
-
-
-def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
- """Run command for each module within execd_dir which defines it."""
- if stderr is None:
- stderr = sys.stdout
- for submodule_path in execd_submodule_paths(command, execd_dir):
- # Only run each execd once. We cannot simply run them in the
- # install hook, as potentially storage hooks are run before that.
- # We cannot rely on them being idempotent.
- sentinel = execd_sentinel_path(submodule_path)
- if os.path.exists(sentinel):
- continue
-
- try:
- subprocess.check_call([submodule_path], stderr=stderr,
- universal_newlines=True)
- with open(sentinel, 'w') as f:
- f.write('{} ran successfully {}\n'.format(submodule_path,
- time.ctime()))
- f.write('Removing this file will cause it to be run again\n')
- except subprocess.CalledProcessError as e:
- # Logs get the details. We can't use juju-log, as the
- # output may be substantial and exceed command line
- # length limits.
- print("ERROR ({}) running {}".format(e.returncode, e.cmd),
- file=stderr)
- print("STDOUT<<EOM", file=stderr)
- print(e.output, file=stderr)
- print("EOM", file=stderr)
-
- # Unit workload status gets a shorter fail message.
- short_path = os.path.relpath(submodule_path)
- block_msg = "Error ({}) running {}".format(e.returncode,
- short_path)
- try:
- subprocess.check_call(['status-set', 'blocked', block_msg],
- universal_newlines=True)
- if stop_on_error:
- sys.exit(0) # Leave unit in blocked state.
- except Exception:
- pass # We care about the exec.d/* failure, not status-set.
-
- if stop_on_error:
- sys.exit(e.returncode or 1) # Error state for pre-1.24 Juju
-
-
-def execd_preinstall(execd_dir=None):
- """Run charm-pre-install for each module within execd_dir."""
- execd_run('charm-pre-install', execd_dir=execd_dir)
stdout, stderr = p.communicate()
retcode = p.poll()
if retcode > 0:
- raise subprocess.CalledProcessError(
- returncode=retcode,
- cmd=cmd,
- output=stderr.decode("utf-8").strip())
+ raise subprocess.CalledProcessError(returncode=retcode,
+ cmd=cmd,
+ output=stderr.decode("utf-8").strip())
return (''.join(stdout), ''.join(stderr))
maintainers:
- Marco Ceppi <marco.ceppi@canonical.com>
- Adam Israel <adam.israel@canonical.com>
- - Philip Joseph <philip.joseph@riftio.com>
summary: setup a virtualized PE Router with GRE tunnels
description: |
this charm, when deployed and configured, will provide a secure virtualized
)
from charms.reactive import (
+ hook,
when,
when_not,
helpers,
cfg = config()
-@when('config.changed')
+@hook('config-changed')
def validate_config():
try:
"""
If the ssh credentials are available, we'll act as a proxy charm.
Otherwise, we execute against the unit we're deployed on to.
"""
- status_set('maintenance', 'configuring ssh connection')
- remove_state('vpe.configured')
if all(k in cfg for k in ['pass', 'vpe-router', 'user']):
routerip = cfg['vpe-router']
user = cfg['user']
(' '.join(e.cmd), str(e.output)))
raise
- set_state('vpe.configured')
- status_set('active', 'ready!')
+ set_state('vpe.configured')
+ status_set('active', 'ready!')
except Exception as e:
log(repr(e))
+++ /dev/null
-flake8
-pytest
+++ /dev/null
-[tox]
-skipsdist=True
-envlist = py34, py35
-skip_missing_interpreters = True
-
-[testenv]
-commands = py.test -v
-deps =
- -r{toxinidir}/requirements.txt
-
-[flake8]
-exclude=docs
--- /dev/null
+paramiko>=1.16.0,<1.17