Refactor repo to not use builds/ directory for compiled charms due to licensing conflicts. See README.md for instructions on how to build a layer

Signed-off-by: Adam Israel <adam.israel@canonical.com>
diff --git a/layers/vyos-proxy/Makefile b/layers/vyos-proxy/Makefile
new file mode 100644
index 0000000..a1ad3a5
--- /dev/null
+++ b/layers/vyos-proxy/Makefile
@@ -0,0 +1,24 @@
+#!/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
diff --git a/layers/vyos-proxy/README.md b/layers/vyos-proxy/README.md
new file mode 100644
index 0000000..0337c83
--- /dev/null
+++ b/layers/vyos-proxy/README.md
@@ -0,0 +1,221 @@
+# 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
diff --git a/layers/vyos-proxy/actions.yaml b/layers/vyos-proxy/actions.yaml
new file mode 100644
index 0000000..2acc33d
--- /dev/null
+++ b/layers/vyos-proxy/actions.yaml
@@ -0,0 +1,12 @@
+"ping":
+  "description": "ping a thing!"
+  "params":
+    "count":
+      "description": "Stop after sending count ECHO_REQUEST packets"
+      "type": "integer"
+      "default": !!int "30"
+    "destination":
+      "description": "destination of ping request"
+      "type": "string"
+  "required":
+  - "destination"
diff --git a/layers/vyos-proxy/actions/ping b/layers/vyos-proxy/actions/ping
new file mode 100755
index 0000000..9850fe7
--- /dev/null
+++ b/layers/vyos-proxy/actions/ping
@@ -0,0 +1,18 @@
+#!/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('actions.ping')
+
+try:
+    main()
+except Exception as e:
+    action_fail(repr(e))
diff --git a/layers/vyos-proxy/config.yaml b/layers/vyos-proxy/config.yaml
new file mode 100644
index 0000000..0780d5f
--- /dev/null
+++ b/layers/vyos-proxy/config.yaml
@@ -0,0 +1,13 @@
+"options":
+  "hostname":
+    "default": !!null ""
+    "type": "string"
+    "description": "Hostname or IP of the VyOS"
+  "user":
+    "type": "string"
+    "default": "vyos"
+    "description": "Username for VyOS"
+  "pass":
+    "type": "string"
+    "default": !!null ""
+    "description": "Password for VyOS"
diff --git a/layers/vyos-proxy/copyright b/layers/vyos-proxy/copyright
new file mode 100644
index 0000000..afa853f
--- /dev/null
+++ b/layers/vyos-proxy/copyright
@@ -0,0 +1,9 @@
+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'
diff --git a/layers/vyos-proxy/icon.svg b/layers/vyos-proxy/icon.svg
new file mode 100644
index 0000000..e092eef
--- /dev/null
+++ b/layers/vyos-proxy/icon.svg
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>

+<!-- Created with Inkscape (http://www.inkscape.org/) -->

+

+<svg

+   xmlns:dc="http://purl.org/dc/elements/1.1/"

+   xmlns:cc="http://creativecommons.org/ns#"

+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

+   xmlns:svg="http://www.w3.org/2000/svg"

+   xmlns="http://www.w3.org/2000/svg"

+   xmlns:xlink="http://www.w3.org/1999/xlink"

+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"

+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"

+   width="96"

+   height="96"

+   id="svg6517"

+   version="1.1"

+   inkscape:version="0.48+devel r12274"

+   sodipodi:docname="Juju_charm_icon_template.svg">

+  <defs

+     id="defs6519">

+    <linearGradient

+       inkscape:collect="always"

+       xlink:href="#Background"

+       id="linearGradient6461"

+       gradientUnits="userSpaceOnUse"

+       x1="0"

+       y1="970.29498"

+       x2="144"

+       y2="970.29498"

+       gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />

+    <linearGradient

+       id="Background">

+      <stop

+         id="stop4178"

+         offset="0"

+         style="stop-color:#b8b8b8;stop-opacity:1" />

+      <stop

+         id="stop4180"

+         offset="1"

+         style="stop-color:#c9c9c9;stop-opacity:1" />

+    </linearGradient>

+    <filter

+       style="color-interpolation-filters:sRGB;"

+       inkscape:label="Inner Shadow"

+       id="filter1121">

+      <feFlood

+         flood-opacity="0.59999999999999998"

+         flood-color="rgb(0,0,0)"

+         result="flood"

+         id="feFlood1123" />

+      <feComposite

+         in="flood"

+         in2="SourceGraphic"

+         operator="out"

+         result="composite1"

+         id="feComposite1125" />

+      <feGaussianBlur

+         in="composite1"

+         stdDeviation="1"

+         result="blur"

+         id="feGaussianBlur1127" />

+      <feOffset

+         dx="0"

+         dy="2"

+         result="offset"

+         id="feOffset1129" />

+      <feComposite

+         in="offset"

+         in2="SourceGraphic"

+         operator="atop"

+         result="composite2"

+         id="feComposite1131" />

+    </filter>

+    <filter

+       style="color-interpolation-filters:sRGB;"

+       inkscape:label="Drop Shadow"

+       id="filter950">

+      <feFlood

+         flood-opacity="0.25"

+         flood-color="rgb(0,0,0)"

+         result="flood"

+         id="feFlood952" />

+      <feComposite

+         in="flood"

+         in2="SourceGraphic"

+         operator="in"

+         result="composite1"

+         id="feComposite954" />

+      <feGaussianBlur

+         in="composite1"

+         stdDeviation="1"

+         result="blur"

+         id="feGaussianBlur956" />

+      <feOffset

+         dx="0"

+         dy="1"

+         result="offset"

+         id="feOffset958" />

+      <feComposite

+         in="SourceGraphic"

+         in2="offset"

+         operator="over"

+         result="composite2"

+         id="feComposite960" />

+    </filter>

+    <clipPath

+       clipPathUnits="userSpaceOnUse"

+       id="clipPath873">

+      <g

+         transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"

+         id="g875"

+         inkscape:label="Layer 1"

+         style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">

+        <path

+           style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"

+           d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"

+           id="path877"

+           inkscape:connector-curvature="0"

+           sodipodi:nodetypes="sssssssss" />

+      </g>

+    </clipPath>

+    <filter

+       inkscape:collect="always"

+       id="filter891"

+       inkscape:label="Badge Shadow">

+      <feGaussianBlur

+         inkscape:collect="always"

+         stdDeviation="0.71999962"

+         id="feGaussianBlur893" />

+    </filter>

+  </defs>

+  <sodipodi:namedview

+     id="base"

+     pagecolor="#ffffff"

+     bordercolor="#666666"

+     borderopacity="1.0"

+     inkscape:pageopacity="0.0"

+     inkscape:pageshadow="2"

+     inkscape:zoom="4.0745362"

+     inkscape:cx="18.514671"

+     inkscape:cy="49.018169"

+     inkscape:document-units="px"

+     inkscape:current-layer="layer1"

+     showgrid="true"

+     fit-margin-top="0"

+     fit-margin-left="0"

+     fit-margin-right="0"

+     fit-margin-bottom="0"

+     inkscape:window-width="1920"

+     inkscape:window-height="1029"

+     inkscape:window-x="0"

+     inkscape:window-y="24"

+     inkscape:window-maximized="1"

+     showborder="true"

+     showguides="true"

+     inkscape:guide-bbox="true"

+     inkscape:showpageshadow="false">

+    <inkscape:grid

+       type="xygrid"

+       id="grid821" />

+    <sodipodi:guide

+       orientation="1,0"

+       position="16,48"

+       id="guide823" />

+    <sodipodi:guide

+       orientation="0,1"

+       position="64,80"

+       id="guide825" />

+    <sodipodi:guide

+       orientation="1,0"

+       position="80,40"

+       id="guide827" />

+    <sodipodi:guide

+       orientation="0,1"

+       position="64,16"

+       id="guide829" />

+  </sodipodi:namedview>

+  <metadata

+     id="metadata6522">

+    <rdf:RDF>

+      <cc:Work

+         rdf:about="">

+        <dc:format>image/svg+xml</dc:format>

+        <dc:type

+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />

+        <dc:title></dc:title>

+      </cc:Work>

+    </rdf:RDF>

+  </metadata>

+  <g

+     inkscape:label="BACKGROUND"

+     inkscape:groupmode="layer"

+     id="layer1"

+     transform="translate(268,-635.29076)"

+     style="display:inline">

+    <path

+       style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"

+       d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"

+       id="path6455"

+       inkscape:connector-curvature="0"

+       sodipodi:nodetypes="sssssssss" />

+  </g>

+  <g

+     inkscape:groupmode="layer"

+     id="layer3"

+     inkscape:label="PLACE YOUR PICTOGRAM HERE"

+     style="display:inline" />

+  <g

+     inkscape:groupmode="layer"

+     id="layer2"

+     inkscape:label="BADGE"

+     style="display:none"

+     sodipodi:insensitive="true">

+    <g

+       style="display:inline"

+       transform="translate(-340.00001,-581)"

+       id="g4394"

+       clip-path="none">

+      <g

+         id="g855">

+        <g

+           inkscape:groupmode="maskhelper"

+           id="g870"

+           clip-path="url(#clipPath873)"

+           style="opacity:0.6;filter:url(#filter891)">

+          <path

+             transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"

+             d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"

+             sodipodi:ry="12"

+             sodipodi:rx="12"

+             sodipodi:cy="552.36218"

+             sodipodi:cx="252"

+             id="path844"

+             style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"

+             sodipodi:type="arc" />

+        </g>

+        <g

+           id="g862">

+          <path

+             sodipodi:type="arc"

+             style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"

+             id="path4398"

+             sodipodi:cx="252"

+             sodipodi:cy="552.36218"

+             sodipodi:rx="12"

+             sodipodi:ry="12"

+             d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"

+             transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />

+          <path

+             transform="matrix(1.25,0,0,1.25,33,-100.45273)"

+             d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"

+             sodipodi:ry="12"

+             sodipodi:rx="12"

+             sodipodi:cy="552.36218"

+             sodipodi:cx="252"

+             id="path4400"

+             style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"

+             sodipodi:type="arc" />

+          <path

+             sodipodi:type="star"

+             style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"

+             id="path4459"

+             sodipodi:sides="5"

+             sodipodi:cx="666.19574"

+             sodipodi:cy="589.50385"

+             sodipodi:r1="7.2431178"

+             sodipodi:r2="4.3458705"

+             sodipodi:arg1="1.0471976"

+             sodipodi:arg2="1.6755161"

+             inkscape:flatsided="false"

+             inkscape:rounded="0.1"

+             inkscape:randomized="0"

+             d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"

+             transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />

+        </g>

+      </g>

+    </g>

+  </g>

+</svg>

diff --git a/layers/vyos-proxy/layer.yaml b/layers/vyos-proxy/layer.yaml
new file mode 100644
index 0000000..16ac9ce
--- /dev/null
+++ b/layers/vyos-proxy/layer.yaml
@@ -0,0 +1,12 @@
+"options":
+  "basic":
+    "packages":
+    - "python-dev"
+    - "libffi-dev"
+    - "libssl-dev"
+    "use_venv": !!bool "false"
+    "include_system_packages": !!bool "false"
+  "vyos-proxy": {}
+"includes":
+- "layer:basic"
+"is": "vyos-proxy"
diff --git a/layers/vyos-proxy/metadata.yaml b/layers/vyos-proxy/metadata.yaml
new file mode 100644
index 0000000..1d29b37
--- /dev/null
+++ b/layers/vyos-proxy/metadata.yaml
@@ -0,0 +1,12 @@
+"name": "vyos-proxy"
+"summary": "VyOS Proxy charm for ping"
+"description": |
+  VyOS Proxy charm
+"tags":
+- "vnf"
+- "network"
+"maintainers":
+- "Marco Ceppi <marco.ceppi@canonical.com>"
+"series":
+- "xenial"
+- "trusty"
diff --git a/layers/vyos-proxy/reactive/__init__.py b/layers/vyos-proxy/reactive/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/layers/vyos-proxy/reactive/__init__.py
diff --git a/layers/vyos-proxy/reactive/vyos_proxy.py b/layers/vyos-proxy/reactive/vyos_proxy.py
new file mode 100644
index 0000000..a8fd5e0
--- /dev/null
+++ b/layers/vyos-proxy/reactive/vyos_proxy.py
@@ -0,0 +1,99 @@
+
+import subprocess
+import paramiko
+
+from charmhelpers.core.hookenv import (
+    config,
+    status_set,
+    action_get,
+    action_set,
+    action_fail,
+)
+
+from charms.reactive import (
+    when,
+    when_not,
+    set_state as set_flag,
+    remove_state as remove_flag,
+)
+
+
+@when('config.changed')
+def test_connection():
+    status_set('maintenance', 'configuring ssh connection')
+    remove_flag('vyos-proxy.ready')
+    try:
+        who, _ = run('whoami')
+    except MgmtNotConfigured as e:
+        remove_flag('vyos-proxy.configured')
+        status_set('blocked', str(e))
+    except subprocess.CalledProcessError as e:
+        remove_flag('vyos-proxy.configured')
+        status_set('blocked', e.output)
+    else:
+        set_flag('vyos-proxy.configured')
+
+
+@when('vyos-proxy.configured')
+@when_not('vyos-proxy.ready')
+def vyos_proxy_ready():
+    status_set('active', 'ready')
+    set_flag('vyos-proxy.ready')
+
+
+@when('actions.ping')
+@when_not('vyos-proxy.configured')
+def pingme():
+    action_fail('proxy is not ready')
+
+
+@when('actions.ping')
+@when('vyos-proxy.configured')
+def pingme_forreal():
+    try:
+        result, err = run('ping -qc {} {}'.format(action_get('count'), action_get('destination')))
+    except:
+        action_fail('ping command failed')
+    finally:
+        remove_flag('actions.ping')
+
+    # Here you can send results back from ping, if you had time to parse it
+    action_set({'output': result})
+
+
+
+class MgmtNotConfigured(Exception):
+    pass
+
+
+def run(cmd):
+    ''' 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 '''
+
+    cfg = config()
+
+    hostname = cfg.get('hostname')
+    password = cfg.get('pass')
+    username = cfg.get('user')
+
+    if not (username and password and hostname):
+        raise MgmtNotConfigured('incomplete remote credentials')
+
+    client = paramiko.SSHClient()
+    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+
+    try:
+        client.connect(cfg.get('hostname'), port=22,
+                       username=cfg.get('user'), password=cfg.get('pass'))
+    except paramiko.ssh_exception.AuthenticationException:
+        raise MgmtNotConfigured('invalid credentials')
+
+    stdin, stdout, stderr = client.exec_command(cmd)
+    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))
diff --git a/layers/vyos-proxy/requirements.txt b/layers/vyos-proxy/requirements.txt
new file mode 100644
index 0000000..28ecaca
--- /dev/null
+++ b/layers/vyos-proxy/requirements.txt
@@ -0,0 +1,2 @@
+flake8
+pytest
diff --git a/layers/vyos-proxy/revision b/layers/vyos-proxy/revision
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/layers/vyos-proxy/revision
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/layers/vyos-proxy/tox.ini b/layers/vyos-proxy/tox.ini
new file mode 100644
index 0000000..0b8b27a
--- /dev/null
+++ b/layers/vyos-proxy/tox.ini
@@ -0,0 +1,12 @@
+[tox]
+skipsdist=True
+envlist = py34, py35
+skip_missing_interpreters = True
+
+[testenv]
+commands = py.test -v
+deps =
+    -r{toxinidir}/requirements.txt
+
+[flake8]
+exclude=docs
diff --git a/layers/vyos-proxy/wheelhouse.txt b/layers/vyos-proxy/wheelhouse.txt
new file mode 100644
index 0000000..24d2c9c
--- /dev/null
+++ b/layers/vyos-proxy/wheelhouse.txt
@@ -0,0 +1 @@
+paramiko==2.0.1