+++ /dev/null
-{
- "layers": [
- "layer:basic",
- "vpe-router",
- "build"
- ],
- "signatures": {
- "wheelhouse/ecdsa-0.13.tar.gz": [
- "vpe-router",
- "dynamic",
- "64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa"
- ],
- "hooks/leader-settings-changed": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "deps/layer/layer-basic/copyright": [
- "vpe-router",
- "static",
- "1e2afbd75c71affa132ae7ee3327cb29b5e4b9d9705f27dfd03857c326f50c5c"
- ],
- "deps/layer/layer-basic/Makefile": [
- "vpe-router",
- "static",
- "b7ab3a34e5faf79b96a8632039a0ad0aa87f2a9b5f0ba604e007cafb22190301"
- ],
- "lib/charms/layer/execd.py": [
- "layer:basic",
- "static",
- "c0eb4c90e93a14f04d851f78bc6592e093226321fbe328125958633397aed014"
- ],
- "hooks/leader-elected": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "deps/layer/layer-basic/hooks/install": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "Makefile": [
- "layer:basic",
- "static",
- "b7ab3a34e5faf79b96a8632039a0ad0aa87f2a9b5f0ba604e007cafb22190301"
- ],
- "reactive/__init__.py": [
- "layer:basic",
- "static",
- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
- ],
- "deps/layer/layer-basic/hooks/stop": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "wheelhouse/paramiko-1.16.3.tar.gz": [
- "vpe-router",
- "dynamic",
- "97d932fdb4fec9aadf6bea368123f3ee15b92199f92eb62666370c7fed62d072"
- ],
- "bin/layer_option": [
- "layer:basic",
- "static",
- "621b556cd208005e131e9f648859294347da9376609745a73ca2e808dd2032f9"
- ],
- ".build.manifest": [
- "build",
- "dynamic",
- "unchecked"
- ],
- "actions/delete-corporation": [
- "vpe-router",
- "static",
- "4f74675749113846e3602919aa7d536510f6995877474f5b377629a767792030"
- ],
- "deps/layer/layer-basic/lib/charms/layer/execd.py": [
- "vpe-router",
- "static",
- "c0eb4c90e93a14f04d851f78bc6592e093226321fbe328125958633397aed014"
- ],
- "hooks/upgrade-charm": [
- "layer:basic",
- "static",
- "8b9c153a61cc78067af8d016a8fa1c764caad3a40c82bb0b5121fc4c4c6a04f1"
- ],
- "wheelhouse/charms.reactive-0.4.4.tar.gz": [
- "layer:basic",
- "dynamic",
- "97455ce944a85adb2ceb897af81a1b93746f43527e6c4ac44798b193132158c7"
- ],
- "deps/layer/layer-basic/wheelhouse.txt": [
- "vpe-router",
- "static",
- "2f19b10e72d7299f4fc53c33caa0ad425ebcef59231238b49bc45b10ab49ae16"
- ],
- "wheelhouse/Tempita-0.5.2.tar.gz": [
- "layer:basic",
- "dynamic",
- "cacecf0baa674d356641f1d406b8bff1d756d739c46b869a54de515d08e6fc9c"
- ],
- "actions.yaml": [
- "vpe-router",
- "dynamic",
- "8124c33231f4b04673ec658b09e9fd393f1012dcf7bdeaf6ce23518b976c2b2a"
- ],
- "deps/layer/layer-basic/README.md": [
- "vpe-router",
- "static",
- "83d8c3dbf1dd3be70ba9774a16780d130ebdcfc722d89d86ed596e7feac70c9f"
- ],
- "hooks/hook.template": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "tox.ini": [
- "layer:basic",
- "static",
- "00b57a776ac313e92a52daa05d1fdb6056c21b3105b643d32610fd937664321b"
- ],
- "metadata.yaml": [
- "vpe-router",
- "dynamic",
- "0aaad2d64b51036f39d17b3120c38b498de270c6363db154477ea76d308e3709"
- ],
- "copyright": [
- "layer:basic",
- "static",
- "1e2afbd75c71affa132ae7ee3327cb29b5e4b9d9705f27dfd03857c326f50c5c"
- ],
- "deps/layer/layer-basic/hooks/hook.template": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "wheelhouse/PyYAML-3.12.tar.gz": [
- "layer:basic",
- "dynamic",
- "592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab"
- ],
- "wheelhouse/netaddr-0.7.18.tar.gz": [
- "layer:basic",
- "dynamic",
- "a1f5c9fcf75ac2579b9995c843dade33009543c04f218ff7c007b3c81695bd19"
- ],
- "deps/layer/layer-basic/bin/layer_option": [
- "vpe-router",
- "static",
- "621b556cd208005e131e9f648859294347da9376609745a73ca2e808dd2032f9"
- ],
- ".gitignore": [
- "layer:basic",
- "static",
- "0da5c4dcda27cd6406e5bb81cbf68ddccaf728ac764ec15053a165c1449d87d9"
- ],
- "hooks/install": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "wheelhouse/MarkupSafe-0.23.tar.gz": [
- "layer:basic",
- "dynamic",
- "a4ec1aff59b95a14b45eb2e23761a0179e98319da5a7eb76b56ea8cdc7b871c3"
- ],
- "deps/layer/layer-basic/hooks/config-changed": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "wheelhouse/pyaml-16.9.0.tar.gz": [
- "layer:basic",
- "dynamic",
- "f8d394144d3bc5360cd48f54bfaa2d100a47040216d6f8703c17bd4b235e9bb3"
- ],
- "deps/layer/layer-basic/metadata.yaml": [
- "vpe-router",
- "static",
- "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
- ],
- "hooks/config-changed": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "hooks/stop": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "wheelhouse/six-1.10.0.tar.gz": [
- "layer:basic",
- "dynamic",
- "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a"
- ],
- "lib/charms/layer/basic.py": [
- "layer:basic",
- "static",
- "32204327952d4ea4ef1063f45bdbe7be379a7a7b8220f3e5f2f5e11003b25654"
- ],
- "wheelhouse/pycrypto-2.6.1.tar.gz": [
- "vpe-router",
- "dynamic",
- "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
- ],
- "deps/layer/layer-basic/hooks/update-status": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "lib/charms/router.py": [
- "vpe-router",
- "static",
- "f9afb193fa59c6b23558e11b7f680bcff79f9567f474fa14ba3202c974fa0cc2"
- ],
- "deps/layer/layer-basic/hooks/upgrade-charm": [
- "vpe-router",
- "static",
- "8b9c153a61cc78067af8d016a8fa1c764caad3a40c82bb0b5121fc4c4c6a04f1"
- ],
- "wheelhouse/Jinja2-2.8.tar.gz": [
- "layer:basic",
- "dynamic",
- "bc1ff2ff88dbfacefde4ddde471d1417d3b304e8df103a7a9437d47269201bf4"
- ],
- "deps/layer/layer-basic/hooks/start": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "actions/add-corporation": [
- "vpe-router",
- "static",
- "e17238f57178836974b3dff358318ffefdacc54988fbf6099c454be06e7fcf92"
- ],
- "wheelhouse/pip-8.1.2.tar.gz": [
- "layer:basic",
- "dynamic",
- "4d24b03ffa67638a3fa931c09fd9e0273ffa904e95ebebe7d4b1a54c93d7b732"
- ],
- "deps/layer/layer-basic/hooks/leader-settings-changed": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "hooks/update-status": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "deps/layer/layer-basic/lib/charms/layer/__init__.py": [
- "vpe-router",
- "static",
- "4ffd74384bd81d737572acf2d14fe55431eab8f49d6212d9aabedf24e1d992b2"
- ],
- "deps/layer/layer-basic/.gitignore": [
- "vpe-router",
- "static",
- "0da5c4dcda27cd6406e5bb81cbf68ddccaf728ac764ec15053a165c1449d87d9"
- ],
- "deps/layer/layer-basic/lib/charms/layer/basic.py": [
- "vpe-router",
- "static",
- "32204327952d4ea4ef1063f45bdbe7be379a7a7b8220f3e5f2f5e11003b25654"
- ],
- "README.md": [
- "vpe-router",
- "static",
- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
- ],
- "deps/layer/layer-basic/reactive/__init__.py": [
- "vpe-router",
- "static",
- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
- ],
- "actions/configure-interface": [
- "vpe-router",
- "static",
- "760002fbc2c7a666e0698f2e7179d5c2286151f0e4dd66dfb7465fafb050a55d"
- ],
- "deps/layer/layer-basic/tox.ini": [
- "vpe-router",
- "static",
- "00b57a776ac313e92a52daa05d1fdb6056c21b3105b643d32610fd937664321b"
- ],
- "hooks/start": [
- "layer:basic",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "deps/layer/layer-basic/hooks/leader-elected": [
- "vpe-router",
- "static",
- "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
- ],
- "actions/connect-domains": [
- "vpe-router",
- "static",
- "26e2825afa84388e333c09a21d3ffa1ca1830e0c4c9738bdb5ffe1c946672200"
- ],
- "reactive/vpe_router.py": [
- "vpe-router",
- "static",
- "1f1312855d6b44d4f7578b3b3e43271ea15865ab6eced72b74ce6f87ca523030"
- ],
- "deps/layer/layer-basic/requirements.txt": [
- "vpe-router",
- "static",
- "0f1c70d27e26005a96d66ad54482877ae20f7737693c833e29dd72bd6ac24892"
- ],
- "deps/layer/layer-basic/layer.yaml": [
- "vpe-router",
- "static",
- "fbd3e3a22f3b8b1e7656e12f5db0ebf69c145b8e8b59c125e6faf043cba50b9b"
- ],
- "config.yaml": [
- "vpe-router",
- "dynamic",
- "826c6a9af5ccee2dbcc0dd890f2c7dbc9ffee81946c2e3f3944cc612d6115f14"
- ],
- "layer.yaml": [
- "vpe-router",
- "dynamic",
- "175d7cdddccf1f1e7b3df8caf7098a39d05000525c61d7e6eee1c81bc5d1966c"
- ],
- "lib/charms/layer/__init__.py": [
- "layer:basic",
- "static",
- "4ffd74384bd81d737572acf2d14fe55431eab8f49d6212d9aabedf24e1d992b2"
- ],
- "wheelhouse/charmhelpers-0.9.1.tar.gz": [
- "layer:basic",
- "dynamic",
- "60de745f3ff1424b9aa294c9d28d0b863f0827314331f330264ecc9a89142218"
- ],
- "actions/delete-domain-connection": [
- "vpe-router",
- "static",
- "b7f42af9d853c5e69e691b7ee56691984730334436f88c97689318038940db4e"
- ],
- "requirements.txt": [
- "layer:basic",
- "static",
- "0f1c70d27e26005a96d66ad54482877ae20f7737693c833e29dd72bd6ac24892"
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-*.pyc
-*~
-.ropeproject
-.settings
-.tox
+++ /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)
- @charm proof
-
-.PHONY: unit_test
-unit_test: apt_prereqs
- @echo Starting tests...
- tox
+++ /dev/null
-"configure-interface":
- "description": "Configure an ethernet interface."
- "params":
- "iface-name":
- "type": "string"
- "description": "Device name, e.g. eth1"
- "cidr":
- "type": "string"
- "description": "Network range to assign to the interface"
- "required": ["iface-name"]
-"add-corporation":
- "description": "Add a new corporation to the router"
- "params":
- "domain-name":
- "type": "string"
- "description": "Name of the vlan corporation"
- "iface-name":
- "type": "string"
- "description": "Device name. eg eth1"
- "vlan-id":
- "type": "integer"
- "description": "The name of the vlan?"
- "cidr":
- "type": "string"
- "description": "Network range to assign to the tagged vlan-id"
- "area":
- "type": "string"
- "description": "Link State Advertisements (LSA) type"
- "subnet-cidr":
- "type": "string"
- "description": "Network range"
- "subnet-area":
- "type": "string"
- "description": "Link State Advertisements (LSA) type"
- "required": ["domain-name", "iface-name", "vlan-id", "cidr", "area", "subnet-cidr",
- "subnet-area"]
-"delete-corporation":
- "description": "Remove the corporation from the router completely"
- "params":
- "domain-name":
- "type": "string"
- "description": "The domain of the corporation to remove"
- "cidr":
- "type": "string"
- "description": "Network range to assign to the tagged vlan-id"
- "area":
- "type": "string"
- "description": "Link State Advertisements (LSA) type"
- "subnet-cidr":
- "type": "string"
- "description": "Network range"
- "subnet-area":
- "type": "string"
- "description": "Link State Advertisements (LSA) type"
- "required": ["domain-name", "cidr", "area", "subnet-cidr", "subnet-area"]
-"connect-domains":
- "description": "Connect the router to another router, where the same domain is present"
- "params":
- "domain-name":
- "type": "string"
- "description": "The domain of the coproration to connect"
- "iface-name":
- "type": "string"
- "description": "Device name. eg eth1"
- "tunnel-name":
- "type": "string"
- "description": "Name of the tunnel ?"
- "local-ip":
- "type": "string"
- "description": "local ip ?"
- "remote-ip":
- "type": "string"
- "description": "remote ip ?"
- "tunnel-key":
- "type": "string"
- "description": "tunnel key?"
- "internal-local-ip":
- "type": "string"
- "description": "internal local ip?"
- "internal-remote-ip":
- "type": "string"
- "description": "internal remote ip?"
- "tunnel-type":
- "type": "string"
- "default": "gre"
- "description": "The type of tunnel to establish."
- "required": ["domain-name", "iface-name", "tunnel-name", "local-ip", "remote-ip",
- "tunnel-key", "internal-local-ip", "internal-remote-ip"]
-"delete-domain-connection":
- "description": "Remove the tunnel to another router where the domain is present."
- "params":
- "domain-name":
- "type": "string"
- "description": "The domain of the corporation to unlink"
- "tunnel-name":
- "type": "string"
- "description": "The name of the tunnel to unlink that the domain-name is attached\
- \ to"
- "required": ["domain-name", "tunnel-name"]
+++ /dev/null
-#!/usr/bin/env python3
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main
-from charms.reactive import set_state
-from charmhelpers.core.hookenv import action_fail
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_state('vpe.add-corporation')
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-#!/usr/bin/env python3
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main
-from charms.reactive import set_state
-from charmhelpers.core.hookenv import action_fail
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_state('vpe.configure-interface')
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-#!/usr/bin/env python3
-
-# Load modules from $CHARM_DIR/lib
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main
-from charms.reactive import set_state
-from charmhelpers.core.hookenv import action_fail
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_state('vpe.connect-domains')
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-#!/usr/bin/env python3
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main
-from charms.reactive import set_state
-from charmhelpers.core.hookenv import action_fail
-
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_state('vpe.delete-corporation')
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-#!/usr/bin/env python3
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main
-from charms.reactive import set_state
-from charmhelpers.core.hookenv import action_fail
-
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_state('vpe.delete-domain-connection')
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /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)
+++ /dev/null
-"options":
- "vpe-router":
- "default": !!null ""
- "type": "string"
- "description": "Hostname or IP of the vpe router to connect to"
- "user":
- "type": "string"
- "default": "root"
- "description": "Username for VPE Router"
- "pass":
- "type": "string"
- "default": !!null ""
- "description": "Password for VPE Router"
- "hostname":
- "type": "string"
- "default": !!null ""
- "description": "The hostname to set the vpe router to."
+++ /dev/null
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
-
-Files: *
-Copyright: 2015, Canonical Ltd.
-License: GPL-3
-
-License: GPL-3
- On Debian GNU/Linux system you can find the complete text of the
- GPL-3 license in '/usr/share/common-licenses/GPL-3'
+++ /dev/null
-*.pyc
-*~
-.ropeproject
-.settings
-.tox
+++ /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)
- @charm proof
-
-.PHONY: unit_test
-unit_test: apt_prereqs
- @echo Starting tests...
- tox
+++ /dev/null
-# Overview
-
-This is the base layer for all charms [built using layers][building]. It
-provides all of the standard Juju hooks and runs the
-[charms.reactive.main][charms.reactive] loop for them. It also bootstraps the
-[charm-helpers][] and [charms.reactive][] libraries and all of their
-dependencies for use by the charm.
-
-# Usage
-
-To create a charm layer using this base layer, you need only include it in
-a `layer.yaml` file:
-
-```yaml
-includes: ['layer:basic']
-```
-
-This will fetch this layer from [interfaces.juju.solutions][] and incorporate
-it into your charm layer. You can then add handlers under the `reactive/`
-directory. Note that **any** file under `reactive/` will be expected to
-contain handlers, whether as Python decorated functions or [executables][non-python]
-using the [external handler protocol][].
-
-### Charm Dependencies
-
-Each layer can include a `wheelhouse.txt` file with Python requirement lines.
-For example, this layer's `wheelhouse.txt` includes:
-
-```
-pip>=7.0.0,<8.0.0
-charmhelpers>=0.4.0,<1.0.0
-charms.reactive>=0.1.0,<2.0.0
-```
-
-All of these dependencies from each layer will be fetched (and updated) at build
-time and will be automatically installed by this base layer before any reactive
-handlers are run.
-
-Note that the `wheelhouse.txt` file is intended for **charm** dependencies only.
-That is, for libraries that the charm code itself needs to do its job of deploying
-and configuring the payload. If the payload itself has Python dependencies, those
-should be handled separately, by the charm.
-
-See [PyPI][pypi charms.X] for packages under the `charms.` namespace which might
-be useful for your charm.
-
-### Layer Namespace
-
-Each layer has a reserved section in the `charms.layer.` Python package namespace,
-which it can populate by including a `lib/charms/layer/<layer-name>.py` file or
-by placing files under `lib/charms/layer/<layer-name>/`. (If the layer name
-includes hyphens, replace them with underscores.) These can be helpers that the
-layer uses internally, or it can expose classes or functions to be used by other
-layers to interact with that layer.
-
-For example, a layer named `foo` could include a `lib/charms/layer/foo.py` file
-with some helper functions that other layers could access using:
-
-```python
-from charms.layer.foo import my_helper
-```
-
-### Layer Options
-
-Any layer can define options in its `layer.yaml`. Those options can then be set
-by other layers to change the behavior of your layer. The options are defined
-using [jsonschema][], which is the same way that [action paramters][] are defined.
-
-For example, the `foo` layer could include the following option definitons:
-
-```yaml
-includes: ['layer:basic']
-defines: # define some options for this layer (the layer "foo")
- enable-bar: # define an "enable-bar" option for this layer
- description: If true, enable support for "bar".
- type: boolean
- default: false
-```
-
-A layer using `foo` could then set it:
-
-```yaml
-includes: ['layer:foo']
-options:
- foo: # setting options for the "foo" layer
- enable-bar: true # set the "enable-bar" option to true
-```
-
-The `foo` layer can then use the `charms.layer.options` helper to load the values
-for the options that it defined. For example:
-
-```python
-from charms import layer
-
-@when('state')
-def do_thing():
- layer_opts = layer.options('foo') # load all of the options for the "foo" layer
- if layer_opts['enable-bar']: # check the value of the "enable-bar" option
- hookenv.log("Bar is enabled")
-```
-
-You can also access layer options in other handlers, such as Bash, using
-the command-line interface:
-
-```bash
-. charms.reactive.sh
-
-@when 'state'
-function do_thing() {
- if layer_option foo enable-bar; then
- juju-log "Bar is enabled"
- juju-log "bar-value is: $(layer_option foo bar-value)"
- fi
-}
-
-reactive_handler_main
-```
-
-Note that options of type `boolean` will set the exit code, while other types
-will be printed out.
-
-# Hooks
-
-This layer provides hooks that other layers can react to using the decorators
-of the [charms.reactive][] library:
-
- * `config-changed`
- * `install`
- * `leader-elected`
- * `leader-settings-changed`
- * `start`
- * `stop`
- * `upgrade-charm`
- * `update-status`
-
-Other hooks are not implemented at this time. A new layer can implement storage
-or relation hooks in their own layer by putting them in the `hooks` directory.
-
-**Note:** Because `update-status` is invoked every 5 minutes, you should take
-care to ensure that your reactive handlers only invoke expensive operations
-when absolutely necessary. It is recommended that you use helpers like
-[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure
-that handlers run only when necessary.
-
-# Layer Configuration
-
-This layer supports the following options, which can be set in `layer.yaml`:
-
- * **packages** A list of system packages to be installed before the reactive
- handlers are invoked.
-
- * **use_venv** If set to true, the charm dependencies from the various
- layers' `wheelhouse.txt` files will be installed in a Python virtualenv
- located at `$CHARM_DIR/../.venv`. This keeps charm dependencies from
- conflicting with payload dependencies, but you must take care to preserve
- the environment and interpreter if using `execl` or `subprocess`.
-
- * **include_system_packages** If set to true and using a venv, include
- the `--system-site-packages` options to make system Python libraries
- visible within the venv.
-
-An example `layer.yaml` using these options might be:
-
-```yaml
-includes: ['layer:basic']
-options:
- basic:
- packages: ['git']
- use_venv: true
- include_system_packages: true
-```
-
-
-# Reactive States
-
-This layer will set the following states:
-
- * **`config.changed`** Any config option has changed from its previous value.
- This state is cleared automatically at the end of each hook invocation.
-
- * **`config.changed.<option>`** A specific config option has changed.
- **`<option>`** will be replaced by the config option name from `config.yaml`.
- This state is cleared automatically at the end of each hook invocation.
-
- * **`config.set.<option>`** A specific config option has a True or non-empty
- value set. **`<option>`** will be replaced by the config option name from
- `config.yaml`. This state is cleared automatically at the end of each hook
- invocation.
-
- * **`config.default.<option>`** A specific config option is set to its
- default value. **`<option>`** will be replaced by the config option name
- from `config.yaml`. This state is cleared automatically at the end of
- each hook invocation.
-
-An example using the config states would be:
-
-```python
-@when('config.changed.my-opt')
-def my_opt_changed():
- update_config()
- restart_service()
-```
-
-
-# Actions
-
-This layer currently does not define any actions.
-
-
-[building]: https://jujucharms.com/docs/devel/authors-charm-building
-[charm-helpers]: https://pythonhosted.org/charmhelpers/
-[charms.reactive]: https://pythonhosted.org/charms.reactive/
-[interfaces.juju.solutions]: http://interfaces.juju.solutions/
-[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers
-[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler
-[jsonschema]: http://json-schema.org/
-[action paramters]: https://jujucharms.com/docs/stable/authors-charm-actions
-[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search
-[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once
-[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed
-[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed
+++ /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)
+++ /dev/null
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
-
-Files: *
-Copyright: 2015, Canonical Ltd.
-License: GPL-3
-
-License: GPL-3
- On Debian GNU/Linux system you can find the complete text of the
- GPL-3 license in '/usr/share/common-licenses/GPL-3'
+++ /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()
+++ /dev/null
-defines:
- packages:
- type: array
- default: []
- description: Additional packages to be installed at time of bootstrap
- use_venv:
- type: boolean
- default: false
- description: >
- Install charm dependencies (wheelhouse) into a Python virtual environment
- to help avoid conflicts with other charms or libraries on the machine.
- include_system_packages:
- type: boolean
- default: false
- description: >
- If using a virtual environment, allow the venv to see Python packages
- installed at the system level. This reduces isolation, but is necessary
- to use Python packages installed via apt-get.
+++ /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.safe_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)
+++ /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
-pip>=7.0.0,<8.2.0
-charmhelpers>=0.4.0,<1.0.0
-charms.reactive>=0.1.0,<2.0.0
+++ /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()
+++ /dev/null
-"options":
- "layer-basic":
- "use_venv": !!bool "false"
- "packages": []
- "include_system_packages": !!bool "false"
- "vpe-router": {}
-"includes":
-- "layer:basic"
-"is": "vpe-router"
+++ /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.safe_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)
+++ /dev/null
-
-import paramiko
-import subprocess
-
-from charmhelpers.core.hookenv import config
-
-
-class NetNS(object):
- def __init__(self, name):
- pass
-
- @classmethod
- def create(cls, name):
- # @TODO: Need to check if namespace exists already
- try:
- ip('netns', 'add', name)
- except Exception as e:
- raise Exception('could not create net namespace: %s' % e)
-
- return cls(name)
-
- def up(self, iface, cidr):
- self.do('ip', 'link', 'set', 'dev', iface, 'up')
- self.do('ip', 'address', 'add', cidr, 'dev', iface)
-
- def add_iface(self, iface):
- ip('link', 'set', 'dev', iface, 'netns', self.name)
-
- def do(self, *cmd):
- ip(*['netns', 'exec', self.name] + cmd)
-
-
-def ip(*args):
- return _run(['ip'] + list(args))
-
-
-def _run(cmd, env=None):
- if isinstance(cmd, str):
- cmd = cmd.split() if ' ' in cmd else [cmd]
-
- cfg = config()
- if all(k in cfg for k in ['pass', 'vpe-router', 'user']):
- router = cfg['vpe-router']
- user = cfg['user']
- passwd = cfg['pass']
-
- if router and user and passwd:
- return ssh(cmd, router, user, passwd)
-
- p = subprocess.Popen(cmd,
- env=env,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = p.communicate()
- retcode = p.poll()
- if retcode > 0:
- raise subprocess.CalledProcessError(returncode=retcode,
- cmd=cmd,
- output=stderr.decode("utf-8").strip())
- return (''.join(stdout), ''.join(stderr))
-
-
-def ssh(cmd, host, user, password=None):
- ''' Suddenly this project needs to SSH to something. So we replicate what
- _run was doing with subprocess using the Paramiko library. This is
- temporary until this charm /is/ the VPE Router '''
-
- cmds = ' '.join(cmd)
- client = paramiko.SSHClient()
- client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- client.connect(host, port=22, username=user, password=password)
-
- stdin, stdout, stderr = client.exec_command(cmds)
- retcode = stdout.channel.recv_exit_status()
- client.close() # @TODO re-use connections
- if retcode > 0:
- output = stderr.read().strip()
- raise subprocess.CalledProcessError(returncode=retcode, cmd=cmd,
- output=output)
- return (''.join(stdout), ''.join(stderr))
+++ /dev/null
-"name": "vpe-router"
-"summary": "setup a virtualized PE Router with GRE tunnels"
-"maintainers":
-- "Marco Ceppi <marco.ceppi@canonical.com>"
-- "Adam Israel <adam.israel@canonical.com>"
-"description": |
- this charm, when deployed and configured, will provide a secure virtualized
- provider edge router.
-"peers":
- "loadbalance":
- "interface": "vpe-router"
-"series":
-- "trusty"
-- "xenial"
+++ /dev/null
-
-from charmhelpers.core.hookenv import (
- config,
- status_set,
- action_get,
- action_fail,
- log,
-)
-
-from charms.reactive import (
- hook,
- when,
- when_not,
- helpers,
- set_state,
- remove_state,
-)
-
-from charms import router
-import subprocess
-
-cfg = config()
-
-
-@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.
- """
- if all(k in cfg for k in ['pass', 'vpe-router', 'user']):
- routerip = cfg['vpe-router']
- user = cfg['user']
- passwd = cfg['pass']
-
- if routerip and user and passwd:
- # Assumption: this will be a root user
- out, err = router.ssh(['whoami'], routerip,
- user, passwd)
- if out.strip() != user:
- remove_state('vpe.configured')
- status_set('blocked', 'vpe is not configured')
- raise Exception('invalid credentials')
-
- # Set the router's hostname
- try:
- if user == 'root' and 'hostname' in cfg:
- hostname = cfg['hostname']
- out, err = router.ssh(['hostname', hostname],
- routerip,
- user, passwd)
- out, err = router.ssh(['sed',
- '-i',
- '"s/hostname.*$/hostname %s/"'
- % hostname,
- '/usr/admin/global/hostname.sh'
- ],
- routerip,
- user, passwd)
- set_state('vpe.configured')
- status_set('active', 'ready!')
- else:
- remove_state('vpe.configured')
- status_set('blocked', 'vpe is not configured')
- except subprocess.CalledProcessError as e:
- remove_state('vpe.configured')
- status_set('blocked', 'validation failed: %s' % e)
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- raise
-
- except Exception as e:
- log(repr(e))
- remove_state('vpe.configured')
- status_set('blocked', 'validation failed: %s' % e)
-
-
-@when_not('vpe.configured')
-def not_ready_add():
- actions = [
- 'vpe.add-corporation',
- 'vpe.connect-domains',
- 'vpe.delete-domain-connections',
- 'vpe.remove-corporation',
- 'vpe.configure-interface',
- 'vpe.configure-ospf',
- ]
-
- if helpers.any_states(*actions):
- action_fail('VPE is not configured')
-
- status_set('blocked', 'vpe is not configured')
-
-
-def start_ospfd():
- # We may want to make this configurable via config setting
- ospfd = '/usr/local/bin/ospfd'
-
- try:
- (stdout, stderr) = router._run(['touch',
- '/usr/admin/global/ospfd.conf'])
- (stdout, stderr) = router._run([ospfd, '-d', '-f',
- '/usr/admin/global/ospfd.conf'])
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
-
-
-def configure_ospf(domain, cidr, area, subnet_cidr, subnet_area, enable=True):
- """Configure the OSPF service"""
-
- # Check to see if the OSPF daemon is running, and start it if not
- try:
- (stdout, stderr) = router._run(['pgrep', 'ospfd'])
- except subprocess.CalledProcessError as e:
- # If pgrep fails, the process wasn't found.
- start_ospfd()
- log('Command failed (ospfd not running): %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
-
- upordown = ''
- if not enable:
- upordown = 'no'
- try:
- vrfctl = '/usr/local/bin/vrfctl'
- vtysh = '/usr/local/bin/vtysh'
-
- (stdout, stderr) = router._run([vrfctl, 'list'])
-
- domain_id = 0
- for line in stdout.split('\n'):
- if domain in line:
- domain_id = int(line[3:5])
-
- if domain_id > 0:
- router._run([vtysh,
- '-c',
- '"configure terminal"',
- '-c',
- '"router ospf %d vr %d"' % (domain_id, domain_id),
- '-c',
- '"%s network %s area %s"' % (upordown, cidr, area),
- '-c',
- '"%s network %s area %s"' % (upordown,
- subnet_cidr,
- subnet_area),
- ])
-
- else:
- log("Invalid domain id")
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- finally:
- remove_state('vpe.configure-interface')
- status_set('active', 'ready!')
-
-
-@when('vpe.configured')
-@when('vpe.configure-interface')
-def configure_interface():
- """
- Configure an ethernet interface
- """
- iface_name = action_get('iface-name')
- cidr = action_get('cidr')
-
- # cidr is optional
- if cidr:
- try:
- # Add may fail, but change seems to add or update
- router.ip('address', 'change', cidr, 'dev', iface_name)
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- return
- finally:
- remove_state('vpe.configure-interface')
- status_set('active', 'ready!')
-
- try:
- router.ip('link', 'set', 'dev', iface_name, 'up')
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- finally:
- remove_state('vpe.configure-interface')
- status_set('active', 'ready!')
-
-
-@when('vpe.configured')
-@when('vpe.add-corporation')
-def add_corporation():
- '''
- Create and Activate the network corporation
- '''
- domain_name = action_get('domain-name')
- iface_name = action_get('iface-name')
- # HACK: python's list, used deeper, throws an exception on ints in a tuple
- vlan_id = str(action_get('vlan-id'))
- cidr = action_get('cidr')
- area = action_get('area')
- subnet_cidr = action_get('subnet-cidr')
- subnet_area = action_get('subnet-area')
-
- iface_vlanid = '%s.%s' % (iface_name, vlan_id)
-
- status_set('maintenance', 'adding corporation {}'.format(domain_name))
-
- """
- Attempt to run all commands to add the network corporation. If any step
- fails, abort and call `delete_corporation()` to undo.
- """
- try:
- """
- $ ip link add link eth3 name eth3.103 type vlan id 103
- """
- router.ip('link',
- 'add',
- 'link',
- iface_name,
- 'name',
- iface_vlanid,
- 'type',
- 'vlan',
- 'id',
- vlan_id)
-
- """
- $ ip netns add domain
- """
- router.ip('netns',
- 'add',
- domain_name)
-
- """
- $ ip link set dev eth3.103 netns corpB
- """
- router.ip('link',
- 'set',
- 'dev',
- iface_vlanid,
- 'netns',
- domain_name)
-
- """
- $ ifconfig eth3 up
- """
- router._run(['ifconfig', iface_name, 'up'])
-
- """
- $ ip netns exec corpB ip link set dev eth3.103 up
- """
- router.ip('netns',
- 'exec',
- domain_name,
- 'ip',
- 'link',
- 'set',
- 'dev',
- iface_vlanid,
- 'up')
-
- """
- $ ip netns exec corpB ip address add 10.0.1.1/24 dev eth3.103
- """
- mask = cidr.split("/")[1]
- ip = '%s/%s' % (area, mask)
- router.ip('netns',
- 'exec',
- domain_name,
- 'ip',
- 'address',
- 'add',
- ip,
- 'dev',
- iface_vlanid)
-
- configure_ospf(domain_name, cidr, area, subnet_cidr, subnet_area, True)
-
- except subprocess.CalledProcessError as e:
- delete_corporation()
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- finally:
- remove_state('vpe.add-corporation')
- status_set('active', 'ready!')
-
-
-@when('vpe.configured')
-@when('vpe.delete-corporation')
-def delete_corporation():
-
- domain_name = action_get('domain-name')
- cidr = action_get('cidr')
- area = action_get('area')
- subnet_cidr = action_get('subnet-cidr')
- subnet_area = action_get('subnet-area')
-
- status_set('maintenance', 'deleting corporation {}'.format(domain_name))
-
- try:
- """
- Remove all tunnels defined for this domain
-
- $ ip netns exec domain_name ip tun show
- | grep gre
- | grep -v "remote any"
- | cut -d":" -f1
- """
- p = router.ip(
- 'netns',
- 'exec',
- domain_name,
- 'ip',
- 'tun',
- 'show',
- '|',
- 'grep',
- 'gre',
- '|',
- 'grep',
- '-v',
- '"remote any"',
- '|',
- 'cut -d":" -f1'
- )
-
- # `p` should be a tuple of (stdout, stderr)
- tunnels = p[0].split('\n')
-
- for tunnel in tunnels:
- try:
- """
- $ ip netns exec domain_name ip link set $tunnel_name down
- """
- router.ip(
- 'netns',
- 'exec',
- domain_name,
- 'ip',
- 'link',
- 'set',
- tunnel,
- 'down'
- )
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- pass
-
- try:
- """
- $ ip netns exec domain_name ip tunnel del $tunnel_name
- """
- router.ip(
- 'netns',
- 'exec',
- domain_name,
- 'ip',
- 'tunnel',
- 'del',
- tunnel
- )
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- pass
-
- """
- Remove all interfaces associated to the domain
-
- $ ip netns exec domain_name ifconfig | grep mtu | cut -d":" -f1
- """
- p = router.ip(
- 'netns',
- 'exec',
- domain_name,
- 'ifconfig',
- '|',
- 'grep mtu',
- '|',
- 'cut -d":" -f1'
- )
-
- ifaces = p[0].split('\n')
- for iface in ifaces:
-
- try:
- """
- $ ip netns exec domain_name ip link set $iface down
- """
- router.ip(
- 'netns',
- 'exec',
- domain_name,
- 'ip',
- 'link',
- 'set',
- iface,
- 'down'
- )
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
-
- try:
- """
- $ ifconfig eth3 down
- """
- router._run(['ifconfig', iface, 'down'])
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- pass
-
- try:
- """
- $ ip link del dev $iface
- """
- router.ip(
- 'link',
- 'del',
- 'dev',
- iface
- )
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- pass
-
- try:
- """
- Remove the domain
-
- $ ip netns del domain_name
- """
- router.ip(
- 'netns',
- 'del',
- domain_name
- )
- except subprocess.CalledProcessError as e:
- log('Command failed: %s (%s)' % (' '.join(e.cmd), str(e.output)))
- pass
-
- try:
- configure_ospf(domain_name,
- cidr,
- area,
- subnet_cidr,
- subnet_area,
- False)
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
-
- except:
- # Do nothing
- log('delete-corporation failed.')
- pass
-
- finally:
- remove_state('vpe.delete-corporation')
- status_set('active', 'ready!')
-
-
-@when('vpe.configured')
-@when('vpe.connect-domains')
-def connect_domains():
-
- params = [
- 'domain-name',
- 'iface-name',
- 'tunnel-name',
- 'local-ip',
- 'remote-ip',
- 'tunnel-key',
- 'internal-local-ip',
- 'internal-remote-ip',
- 'tunnel-type',
- ]
-
- config = {}
- for p in params:
- config[p] = action_get(p)
-
- status_set('maintenance', 'connecting domains')
-
- try:
- """
- $ ip tunnel add tunnel_name mode gre local local_ip remote remote_ip
- dev iface_name key tunnel_key csum
- """
- router.ip(
- 'tunnel',
- 'add',
- config['tunnel-name'],
- 'mode',
- config['tunnel-type'],
- 'local',
- config['local-ip'],
- 'remote',
- config['remote-ip'],
- 'dev',
- config['iface-name'],
- 'key',
- config['tunnel-key'],
- 'csum'
- )
-
- except subprocess.CalledProcessError as e:
- log('Command failed (retrying with ip tunnel change): %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- try:
- """
- If the tunnel already exists (like gre0) and can't be deleted,
- modify it instead of trying to add it.
- """
- router.ip(
- 'tunnel',
- 'change',
- config['tunnel-name'],
- 'mode',
- config['tunnel-type'],
- 'local',
- config['local-ip'],
- 'remote',
- config['remote-ip'],
- 'dev',
- config['iface-name'],
- 'key',
- config['tunnel-key'],
- 'csum'
- )
- except subprocess.CalledProcessError as e:
- delete_domain_connection()
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- finally:
- remove_state('vpe.connect-domains')
- status_set('active', 'ready!')
-
- try:
- """
- $ ip link set dev tunnel_name netns domain_name
- """
- router.ip(
- 'link',
- 'set',
- 'dev',
- config['tunnel-name'],
- 'netns',
- config['domain-name']
- )
-
- """
- $ ip netns exec domain_name ip link set dev tunnel_name up
- """
- router.ip(
- 'netns',
- 'exec',
- config['domain-name'],
- 'ip',
- 'link',
- 'set',
- 'dev',
- config['tunnel-name'],
- 'up'
- )
-
- """
- $ ip netns exec domain_name ip address add internal_local_ip peer
- internal_remote_ip dev tunnel_name
- """
- router.ip(
- 'netns',
- 'exec',
- config['domain-name'],
- 'ip',
- 'address',
- 'add',
- config['internal-local-ip'],
- 'peer',
- config['internal-remote-ip'],
- 'dev',
- config['tunnel-name']
- )
- except subprocess.CalledProcessError as e:
- delete_domain_connection()
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- finally:
- remove_state('vpe.connect-domains')
- status_set('active', 'ready!')
-
-
-@when('vpe.configured')
-@when('vpe.delete-domain-connection')
-def delete_domain_connection():
- ''' Remove the tunnel to another router where the domain is present '''
- domain = action_get('domain-name')
- tunnel_name = action_get('tunnel-name')
-
- status_set('maintenance', 'deleting domain connection: {}'.format(domain))
-
- try:
-
- try:
- """
- $ ip netns exec domain_name ip link set tunnel_name down
- """
- router.ip('netns',
- 'exec',
- domain,
- 'ip',
- 'link',
- 'set',
- tunnel_name,
- 'down')
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
-
- try:
- """
- $ ip netns exec domain_name ip tunnel del tunnel_name
- """
- router.ip('netns',
- 'exec',
- domain,
- 'ip',
- 'tunnel',
- 'del',
- tunnel_name)
- except subprocess.CalledProcessError as e:
- action_fail('Command failed: %s (%s)' %
- (' '.join(e.cmd), str(e.output)))
- except:
- pass
- finally:
- remove_state('vpe.delete-domain-connection')
- status_set('active', 'ready!')
+++ /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