Fix bug 760 95/7695/1
authorAdam Israel <adam.israel@canonical.com>
Mon, 24 Jun 2019 15:44:47 +0000 (11:44 -0400)
committerAdam Israel <adam.israel@canonical.com>
Mon, 24 Jun 2019 15:44:47 +0000 (11:44 -0400)
This commit fixes bug 670 by introducing a new PrimitiveDoesNotExist
exception that will be raised if ExecutePrimitive is called but the
primitive does not exist in the charm.

This also bumps the required version of websocket to match libjuju,
along with other minor tweaks to the test framework

Change-Id: I028c3c9c19fbfa87c8feb788446a290d66112043
Signed-off-by: Adam Israel <adam.israel@canonical.com>
Makefile
n2vc/vnf.py
setup.py
tests/base.py
tests/integration/test_non_existent_primitive.py [new file with mode: 0644]

index fab6991..2fb92a8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ clean:
        find . -name *.pyc -delete
        rm -rf .tox
        rm -rf tests/charms/builds/*
-       lxc list test- --format=json|jq '.[]["name"]'| xargs lxc delete --force
+       lxc list test- --format=json|jq '.[]["name"]'| xargs lxc delete --force || true
 .tox:
        tox -r --notest
 test: lint
index 74f4d94..c8ee2ef 100644 (file)
@@ -47,6 +47,9 @@ class NetworkServiceDoesNotExist(Exception):
     """The Network Service being acted against does not exist."""
 
 
+class PrimitiveDoesNotExist(Exception):
+    """The Primitive being executed does not exist."""
+
 # Quiet the debug logging
 logging.getLogger('websockets.protocol').setLevel(logging.INFO)
 logging.getLogger('juju.client.connection').setLevel(logging.WARN)
@@ -723,16 +726,25 @@ class N2VC:
                     }
 
                     for primitive in sorted(primitives):
-                        uuids.append(
-                            await self.ExecutePrimitive(
-                                model_name,
-                                application_name,
-                                primitives[primitive]['name'],
-                                callback,
-                                callback_args,
-                                **primitives[primitive]['parameters'],
+                        try:
+                            # self.log.debug("Queuing action {}".format(primitives[primitive]['name']))
+                            uuids.append(
+                                await self.ExecutePrimitive(
+                                    model_name,
+                                    application_name,
+                                    primitives[primitive]['name'],
+                                    callback,
+                                    callback_args,
+                                    **primitives[primitive]['parameters'],
+                                )
                             )
-                        )
+                        except PrimitiveDoesNotExist as e:
+                            self.log.debug("Ignoring exception PrimitiveDoesNotExist: {}".format(e))
+                            pass
+                        except Exception as e:
+                            self.log.debug("XXXXXXXXXXXXXXXXXXXXXXXXX Unexpected exception: {}".format(e))
+                            raise e
+
             except N2VCPrimitiveExecutionFailed as e:
                 self.log.debug(
                     "[N2VC] Exception executing primitive: {}".format(e)
@@ -779,12 +791,22 @@ class N2VC:
             else:
                 app = await self.get_application(model, application_name)
                 if app:
+                    # Does this primitive exist?
+                    actions = await app.get_actions()
+
+                    if primitive not in actions.keys():
+                        raise PrimitiveDoesNotExist("Primitive {} does not exist".format(primitive))
+
                     # Run against the first (and probably only) unit in the app
                     unit = app.units[0]
                     if unit:
                         action = await unit.run_action(primitive, **params)
                         uuid = action.id
+        except PrimitiveDoesNotExist as e:
+            # Catch and raise this exception if it's thrown from the inner block
+            raise e
         except Exception as e:
+            # An unexpected exception was caught
             self.log.debug(
                 "Caught exception while executing primitive: {}".format(e)
             )
index d836d2f..2111150 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,7 @@ setup(
         'pyRFC3339>=1.0,<2.0',
         'pyyaml>=3.0,<4.0',
         'theblues>=0.3.8,<1.0',
-        'websockets>=4.0,<5.0',
+        'websockets>=7.0,<8.0',
         'paramiko',
         'pyasn1>=0.4.4',
     ],
index 663e89a..ce95056 100644 (file)
@@ -1131,7 +1131,8 @@ class TestN2VC(object):
             return True
         except Exception as ex:
             debug("execute_initial_config_primitives exception: {}".format(ex))
-
+            raise ex
+            
         return False
 
     @classmethod
diff --git a/tests/integration/test_non_existent_primitive.py b/tests/integration/test_non_existent_primitive.py
new file mode 100644 (file)
index 0000000..acd4211
--- /dev/null
@@ -0,0 +1,145 @@
+"""
+Deploy a VNF and execute a non-existent primitive
+"""
+import asyncio
+import logging
+import pytest
+from .. import base
+# import n2vc.vnf
+
+# @pytest.mark.serial
+class TestCharm(base.TestN2VC):
+
+    NSD_YAML = """
+    nsd:nsd-catalog:
+        nsd:
+        -   id: nonexistent-ns
+            name: nonexistent-ns
+            short-name: nonexistent-ns
+            description: NS with 1 VNFs charmnative-vnf connected by datanet and mgmtnet VLs
+            version: '1.0'
+            logo: osm.png
+            constituent-vnfd:
+            -   vnfd-id-ref: charmnative-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: charmnative-vnf
+                    member-vnf-index-ref: '1'
+                    vnfd-connection-point-ref: vnf-mgmt
+                -   vnfd-id-ref: charmnative-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: charmnative-vnf
+                    member-vnf-index-ref: '1'
+                    vnfd-connection-point-ref: vnf-data
+                -   vnfd-id-ref: charmnative-vnf
+                    member-vnf-index-ref: '2'
+                    vnfd-connection-point-ref: vnf-data
+    """
+
+    VNFD_YAML = """
+    vnfd:vnfd-catalog:
+        vnfd:
+        -   id: charmnative-vnf
+            name: charmnative-vnf
+            short-name: charmnative-vnf
+            version: '1.0'
+            description: A VNF consisting of 2 VDUs w/charms 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: xenial
+                count: '1'
+                vm-flavor:
+                    vcpu-count: '1'
+                    memory-mb: '1024'
+                    storage-gb: '10'
+                interface:
+                -   name: mgmtVM-eth0
+                    position: '1'
+                    type: EXTERNAL
+                    virtual-interface:
+                        type: VIRTIO
+                    external-connection-point-ref: vnf-mgmt
+                -   name: mgmtVM-eth1
+                    position: '2'
+                    type: INTERNAL
+                    virtual-interface:
+                        type: VIRTIO
+                    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
+                vdu-configuration:
+                    juju:
+                        charm: native-ci
+                        proxy: false
+                    initial-config-primitive:
+                    -   seq: '1'
+                        name: idonotexist
+                    -   seq: '2'
+                        name: test
+    """
+
+    # @pytest.mark.serial
+    @pytest.mark.asyncio
+    async def test_charm_non_existent_primitive(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_non_string_parameter stopped")
+
+            
+            # assert False
+        return 'ok'