Fix bug 601
This fixes bug 601, where a charm in a broken state would fail to be
removed.
This builds of of the new DestroyNetworkService method, which will
remove a model containing a network service.
There is no way, currently, to resolve errors on an individual charm
through the Juju API (client), but removing the model will force the
removal of a broken charm.
Change-Id: I47f41991ed444395061b5a20e5a51059950e5200
Signed-off-by: Adam Israel <adam.israel@canonical.com>
diff --git a/n2vc/vnf.py b/n2vc/vnf.py
index 6e4aaf3..9cdbb33 100644
--- a/n2vc/vnf.py
+++ b/n2vc/vnf.py
@@ -805,6 +805,10 @@
except JujuError as e:
if "already exists" not in e.message:
raise e
+
+ # Create an observer for this model
+ await self.create_model_monitor(ns_uuid)
+
return True
async def DestroyNetworkService(self, ns_uuid):
@@ -1061,10 +1065,20 @@
self.refcount['model'] += 1
# Create an observer for this model
+ await self.create_model_monitor(model_name)
+
+ return self.models[model_name]
+
+ async def create_model_monitor(self, model_name):
+ """Create a monitor for the model, if none exists."""
+ if not self.authenticated:
+ await self.login()
+
+ if model_name not in self.monitors:
self.monitors[model_name] = VCAMonitor(model_name)
self.models[model_name].add_observer(self.monitors[model_name])
- return self.models[model_name]
+ return True
async def login(self):
"""Login to the Juju controller."""
diff --git a/tests/base.py b/tests/base.py
index e4111eb..41fa191 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -586,6 +586,9 @@
if not self.n2vc:
self.n2vc = get_n2vc(loop=loop)
+ debug("Creating model for Network Service {}".format(self.ns_name))
+ await self.n2vc.CreateNetworkService(self.ns_name)
+
application = self.n2vc.FormatApplicationName(
self.ns_name,
self.vnf_name,
@@ -889,6 +892,8 @@
try:
await self.n2vc.RemoveCharms(self.ns_name, application)
+ await self.n2vc.DestroyNetworkService(self.ns_name)
+
while True:
# Wait for the application to be removed
await asyncio.sleep(10)
diff --git a/tests/charms/layers/broken/README.md b/tests/charms/layers/broken/README.md
new file mode 100644
index 0000000..9234e57
--- /dev/null
+++ b/tests/charms/layers/broken/README.md
@@ -0,0 +1,3 @@
+# Overview
+
+This charm is intended to install and break, requiring it to be removed.
diff --git a/tests/charms/layers/broken/actions.yaml b/tests/charms/layers/broken/actions.yaml
new file mode 100644
index 0000000..6cd6f8c
--- /dev/null
+++ b/tests/charms/layers/broken/actions.yaml
@@ -0,0 +1,9 @@
+touch:
+ description: "Touch a file on the VNF."
+ params:
+ filename:
+ description: "The name of the file to touch."
+ type: string
+ default: ""
+ required:
+ - filename
diff --git a/tests/charms/layers/broken/actions/touch b/tests/charms/layers/broken/actions/touch
new file mode 100755
index 0000000..7e30af4
--- /dev/null
+++ b/tests/charms/layers/broken/actions/touch
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+##
+# Copyright 2016 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+import sys
+sys.path.append('lib')
+
+from charms.reactive import main, set_flag
+from charmhelpers.core.hookenv import action_fail, action_name
+
+"""
+`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_flag('actions.{}'.format(action_name()))
+
+try:
+ main()
+except Exception as e:
+ action_fail(repr(e))
diff --git a/tests/charms/layers/broken/config.yaml b/tests/charms/layers/broken/config.yaml
new file mode 100644
index 0000000..51f2ce4
--- /dev/null
+++ b/tests/charms/layers/broken/config.yaml
@@ -0,0 +1,14 @@
+options:
+ string-option:
+ type: string
+ default: "Default Value"
+ description: "A short description of the configuration option"
+ boolean-option:
+ type: boolean
+ default: False
+ description: "A short description of the configuration option"
+ int-option:
+ type: int
+ default: 9001
+ description: "A short description of the configuration option"
+
diff --git a/tests/charms/layers/broken/icon.svg b/tests/charms/layers/broken/icon.svg
new file mode 100644
index 0000000..e092eef
--- /dev/null
+++ b/tests/charms/layers/broken/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/tests/charms/layers/broken/layer.yaml b/tests/charms/layers/broken/layer.yaml
new file mode 100644
index 0000000..3fed5e2
--- /dev/null
+++ b/tests/charms/layers/broken/layer.yaml
@@ -0,0 +1,4 @@
+includes: ['layer:basic', 'layer:vnfproxy']
+options:
+ basic:
+ use_venv: false
diff --git a/tests/charms/layers/broken/metadata.yaml b/tests/charms/layers/broken/metadata.yaml
new file mode 100644
index 0000000..1780d3f
--- /dev/null
+++ b/tests/charms/layers/broken/metadata.yaml
@@ -0,0 +1,5 @@
+name: broken
+summary: A (broken) simple VNF proxy charm
+maintainer: Adam Israel <adam.israel@canonical.com>
+subordinate: false
+series: ['xenial']
diff --git a/tests/charms/layers/broken/metrics.yaml b/tests/charms/layers/broken/metrics.yaml
new file mode 100644
index 0000000..6ebb605
--- /dev/null
+++ b/tests/charms/layers/broken/metrics.yaml
@@ -0,0 +1,5 @@
+metrics:
+ uptime:
+ type: gauge
+ description: "Uptime of the VNF"
+ command: awk '{print $1}' /proc/uptime
diff --git a/tests/charms/layers/broken/reactive/simple.py b/tests/charms/layers/broken/reactive/simple.py
new file mode 100644
index 0000000..1529eee
--- /dev/null
+++ b/tests/charms/layers/broken/reactive/simple.py
@@ -0,0 +1,45 @@
+from charmhelpers.core.hookenv import (
+ action_get,
+ action_fail,
+ action_set,
+ status_set,
+)
+from charms.reactive import (
+ clear_flag,
+ set_flag,
+ when,
+ when_not,
+)
+import charms.sshproxy
+
+
+@when('sshproxy.configured')
+@when_not('simple.installed')
+def install_simple_proxy_charm():
+ """Post-install actions.
+
+ This function will run when two conditions are met:
+ 1. The 'sshproxy.configured' state is set
+ 2. The 'simple.installed' state is not set
+
+ This ensures that the workload status is set to active only when the SSH
+ proxy is properly configured.
+ """
+ set_flag('simple.installed')
+ status_set('active', 'Ready!')
+
+
+@when('actions.touch')
+def touch():
+ raise Exception("I am broken.")
+ err = ''
+ try:
+ filename = action_get('filename')
+ cmd = ['touch {}'.format(filename)]
+ result, err = charms.sshproxy._run(cmd)
+ except Exception:
+ action_fail('command failed:' + err)
+ else:
+ action_set({'output': result})
+ finally:
+ clear_flag('actions.touch')
diff --git a/tests/charms/layers/broken/tests/00-setup b/tests/charms/layers/broken/tests/00-setup
new file mode 100644
index 0000000..f0616a5
--- /dev/null
+++ b/tests/charms/layers/broken/tests/00-setup
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sudo add-apt-repository ppa:juju/stable -y
+sudo apt-get update
+sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/broken/tests/10-deploy b/tests/charms/layers/broken/tests/10-deploy
new file mode 100644
index 0000000..9a26117
--- /dev/null
+++ b/tests/charms/layers/broken/tests/10-deploy
@@ -0,0 +1,35 @@
+#!/usr/bin/python3
+
+import amulet
+import requests
+import unittest
+
+
+class TestCharm(unittest.TestCase):
+ def setUp(self):
+ self.d = amulet.Deployment()
+
+ self.d.add('simple')
+ self.d.expose('simple')
+
+ self.d.setup(timeout=900)
+ self.d.sentry.wait()
+
+ self.unit = self.d.sentry['simple'][0]
+
+ def test_service(self):
+ # test we can access over http
+ page = requests.get('http://{}'.format(self.unit.info['public-address']))
+ self.assertEqual(page.status_code, 200)
+ # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
+ # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
+ # - .info - An array of the information of that unit from Juju
+ # - .file(PATH) - Get the details of a file on that unit
+ # - .file_contents(PATH) - Get plain text output of PATH file from that unit
+ # - .directory(PATH) - Get details of directory
+ # - .directory_contents(PATH) - List files and folders in PATH on that unit
+ # - .relation(relation, service:rel) - Get relation data from return service
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/integration/test_broken_charm.py b/tests/integration/test_broken_charm.py
new file mode 100644
index 0000000..296096f
--- /dev/null
+++ b/tests/integration/test_broken_charm.py
@@ -0,0 +1,177 @@
+"""
+Test a charm that breaks post-deployment
+"""
+
+import asyncio
+import logging
+import pytest
+from .. import base
+
+
+# @pytest.mark.serial
+class TestCharm(base.TestN2VC):
+
+ NSD_YAML = """
+ nsd:nsd-catalog:
+ nsd:
+ - id: brokencharm-ns
+ name: brokencharm-ns
+ short-name: brokencharm-ns
+ description: NS with 1 VNF connected by datanet and mgmtnet VLs
+ version: '1.0'
+ logo: osm.png
+ constituent-vnfd:
+ - vnfd-id-ref: charmproxy-vnf
+ member-vnf-index: '1'
+ vld:
+ - id: mgmtnet
+ name: mgmtnet
+ short-name: mgmtnet
+ type: ELAN
+ mgmt-network: 'true'
+ vim-network-name: mgmt
+ vnfd-connection-point-ref:
+ - vnfd-id-ref: charmproxy-vnf
+ member-vnf-index-ref: '1'
+ vnfd-connection-point-ref: vnf-mgmt
+ - vnfd-id-ref: charmproxy-vnf
+ member-vnf-index-ref: '2'
+ vnfd-connection-point-ref: vnf-mgmt
+ - id: datanet
+ name: datanet
+ short-name: datanet
+ type: ELAN
+ vnfd-connection-point-ref:
+ - vnfd-id-ref: charmproxy-vnf
+ member-vnf-index-ref: '1'
+ vnfd-connection-point-ref: vnf-data
+ - vnfd-id-ref: charmproxy-vnf
+ member-vnf-index-ref: '2'
+ vnfd-connection-point-ref: vnf-data
+ """
+
+ VNFD_YAML = """
+ vnfd:vnfd-catalog:
+ vnfd:
+ - id: hackfest-simplecharm-vnf
+ name: hackfest-simplecharm-vnf
+ short-name: hackfest-simplecharm-vnf
+ version: '1.0'
+ description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with cloud-init
+ logo: osm.png
+ connection-point:
+ - id: vnf-mgmt
+ name: vnf-mgmt
+ short-name: vnf-mgmt
+ type: VPORT
+ - id: vnf-data
+ name: vnf-data
+ short-name: vnf-data
+ type: VPORT
+ mgmt-interface:
+ cp: vnf-mgmt
+ internal-vld:
+ - id: internal
+ name: internal
+ short-name: internal
+ type: ELAN
+ internal-connection-point:
+ - id-ref: mgmtVM-internal
+ - id-ref: dataVM-internal
+ vdu:
+ - id: mgmtVM
+ name: mgmtVM
+ image: hackfest3-mgmt
+ count: '1'
+ vm-flavor:
+ vcpu-count: '1'
+ memory-mb: '1024'
+ storage-gb: '10'
+ interface:
+ - name: mgmtVM-eth0
+ position: '1'
+ type: EXTERNAL
+ virtual-interface:
+ type: PARAVIRT
+ external-connection-point-ref: vnf-mgmt
+ - name: mgmtVM-eth1
+ position: '2'
+ type: INTERNAL
+ virtual-interface:
+ type: PARAVIRT
+ internal-connection-point-ref: mgmtVM-internal
+ internal-connection-point:
+ - id: mgmtVM-internal
+ name: mgmtVM-internal
+ short-name: mgmtVM-internal
+ type: VPORT
+ cloud-init-file: cloud-config.txt
+ - id: dataVM
+ name: dataVM
+ image: hackfest3-mgmt
+ count: '1'
+ vm-flavor:
+ vcpu-count: '1'
+ memory-mb: '1024'
+ storage-gb: '10'
+ interface:
+ - name: dataVM-eth0
+ position: '1'
+ type: INTERNAL
+ virtual-interface:
+ type: PARAVIRT
+ internal-connection-point-ref: dataVM-internal
+ - name: dataVM-xe0
+ position: '2'
+ type: EXTERNAL
+ virtual-interface:
+ type: PARAVIRT
+ external-connection-point-ref: vnf-data
+ internal-connection-point:
+ - id: dataVM-internal
+ name: dataVM-internal
+ short-name: dataVM-internal
+ type: VPORT
+ vnf-configuration:
+ juju:
+ charm: broken
+ proxy: true
+ initial-config-primitive:
+ - seq: '1'
+ name: touch
+ parameter:
+ - name: filename
+ value: '/home/ubuntu/first-touch'
+ config-primitive:
+ - name: touch
+ parameter:
+ - name: filename
+ data-type: STRING
+ default-value: '/home/ubuntu/touched'
+ """
+
+ # @pytest.mark.serial
+ @pytest.mark.asyncio
+ async def test_charm_proxy(self, event_loop):
+ """Deploy and execute the initial-config-primitive of a VNF."""
+
+ if self.nsd and self.vnfd:
+ vnf_index = 0
+
+ for config in self.get_config():
+ juju = config['juju']
+ charm = juju['charm']
+
+ await self.deploy(
+ vnf_index,
+ charm,
+ config,
+ event_loop,
+ )
+
+ while await self.running():
+ print("Waiting for test to finish...")
+ await asyncio.sleep(15)
+ logging.debug("test_charm_proxy stopped")
+
+ return 'ok'