NFVBENCH-120 No admin support patch 99/66299/4 3.0.5
authorfmenguy <francoisregis.menguy@orange.com>
Wed, 9 Jan 2019 10:22:50 +0000 (11:22 +0100)
committerfmenguy <francoisregis.menguy@orange.com>
Thu, 10 Jan 2019 14:15:05 +0000 (15:15 +0100)
Change-Id: Iaaf29e4eb439243348e955e796b6f951c184ee19
Signed-off-by: fmenguy <francoisregis.menguy@orange.com>
nfvbench/cfg.default.yaml
nfvbench/chaining.py
nfvbench/credentials.py
test/test_chains.py

index fa3d807..c2fa29e 100755 (executable)
 # The only case where this field can be empty is when measuring a system that does not run
 # OpenStack or when OpenStack APIs are not accessible or OpenStack APis use is not
 # desirable. In that case the EXT service chain must be used.
+#
+# If openrc is not admin some parameters are mandatory and must be filled with valid values in config file such as :
+# - availability_zone
+# - hypervisor_hostname
+# - vlans
 openrc_file:
 
 # Forwarder to use in nfvbenchvm image. Available options: ['vpp', 'testpmd']
@@ -66,11 +71,15 @@ flavor:
 # Name of the availability zone to use for the test VMs
 # Must be one of the zones listed by 'nova availability-zone-list'
 # availability_zone: 'nova'
+# If openrc is not admin set a valid value
 availability_zone:
 # To force placement on a given hypervisor, set the name here
 # (if multiple names are provided, the first will be used)
 # Leave empty to let openstack pick the hypervisor
 compute_nodes:
+# If openrc is not admin set a valid value for hypervisor hostname
+# Example of value: hypervisor_hostname: "server1"
+hypervisor_hostname:
 
 # Type of service chain to run, possible options are PVP, PVVP and EXT
 # PVP - port to VM to port
@@ -192,7 +201,7 @@ traffic_generator:
     #
     # Generator profiles are listed in the following format:
     # `name`: Traffic generator profile name (use a unique name, no space or special character)
-    #         DFo not change this field
+    #         Do not change this field
     # `tool`: Traffic generator tool to be used (currently supported is `TRex`).
     #         Do not change this field
     # `ip`: IP address of the traffic generator.
@@ -369,7 +378,7 @@ vxlan: false
 # is not supported). Use the vtep_vlan option to enable vlan tagging for the VxLAN overlay network.
 vlan_tagging: true
 
-# Used only in the case of EXT chain and no openstack to specify the VLAN IDs to use.
+# Used only in the case of EXT chain and no openstack or not admin access to specify the VLAN IDs to use.
 # This property is ignored when OpenStakc is used or in the case of l2-loopback.
 # If OpenStack is used leave the list empty, VLAN IDs are retrieved from OpenStack networks using Neutron API.
 # If networks are shared across all chains (service_chain_shared_net=true), the list should have exactly 2 values
index a97cd0b..1a977da 100644 (file)
@@ -522,7 +522,13 @@ class ChainVnf(object):
 
     def get_hostname(self):
         """Get the hypervisor host name running this VNF instance."""
-        return getattr(self.instance, 'OS-EXT-SRV-ATTR:hypervisor_hostname')
+        if self.manager.is_admin:
+            hypervisor_hostname = getattr(self.instance, 'OS-EXT-SRV-ATTR:hypervisor_hostname')
+        else:
+            hypervisor_hostname = self.manager.config.hypervisor_hostname
+            if not hypervisor_hostname:
+                raise ChainException('Hypervisor hostname parameter is mandatory')
+        return hypervisor_hostname
 
     def get_host_ip(self):
         """Get the IP address of the host where this instance runs.
@@ -536,7 +542,12 @@ class ChainVnf(object):
     def get_hypervisor_name(self):
         """Get hypervisor name (az:hostname) for this VNF instance."""
         if self.instance:
-            az = getattr(self.instance, 'OS-EXT-AZ:availability_zone')
+            if self.manager.is_admin:
+                az = getattr(self.instance, 'OS-EXT-AZ:availability_zone')
+            else:
+                az = self.manager.config.availability_zone
+            if not az:
+                raise ChainException('Availability zone parameter is mandatory')
             hostname = self.get_hostname()
             if az:
                 return az + ':' + hostname
@@ -851,6 +862,7 @@ class ChainManager(object):
         if self.openstack:
             # openstack only
             session = chain_runner.cred.get_session()
+            self.is_admin = chain_runner.cred.is_admin
             self.nova_client = Client(2, session=session)
             self.neutron_client = neutronclient.Client('2.0', session=session)
             self.glance_client = glanceclient.Client('2', session=session)
@@ -877,12 +889,14 @@ class ChainManager(object):
                 else:
                     # Make sure all instances are active before proceeding
                     self._ensure_instances_active()
+                # network API call do not show VLANS ID if not admin read from config
+                if not self.is_admin:
+                    self._get_config_vlans()
             except Exception:
                 self.delete()
                 raise
         else:
             # no openstack, no need to create chains
-
             if not config.l2_loopback and config.no_arp:
                 self._get_dest_macs_from_config()
             if config.vlan_tagging:
@@ -890,12 +904,18 @@ class ChainManager(object):
                 if len(config.vlans) != 2:
                     raise ChainException('The config vlans property must be a list '
                                          'with 2 lists of VLAN IDs')
-                re_vlan = "[0-9]*$"
-                self.vlans = [self._check_list('vlans[0]', config.vlans[0], re_vlan),
-                              self._check_list('vlans[1]', config.vlans[1], re_vlan)]
+                self._get_config_vlans()
             if config.vxlan:
                 raise ChainException('VxLAN is only supported with OpenStack')
 
+    def _get_config_vlans(self):
+        re_vlan = "[0-9]*$"
+        try:
+            self.vlans = [self._check_list('vlans[0]', self.config.vlans[0], re_vlan),
+                          self._check_list('vlans[1]', self.config.vlans[1], re_vlan)]
+        except IndexError:
+            raise ChainException('vlans parameter is mandatory. Set valid value in config file')
+
     def _get_dest_macs_from_config(self):
         re_mac = "[0-9a-fA-F]{2}([-:])[0-9a-fA-F]{2}(\\1[0-9a-fA-F]{2}){4}$"
         tg_config = self.config.traffic_generator
@@ -1114,7 +1134,7 @@ class ChainManager(object):
         port_index: left port is 0, right port is 1
         return: a VLAN ID list indexed by the chain index or None if no vlan tagging
         """
-        if self.chains:
+        if self.chains and self.is_admin:
             return [self.chains[chain_index].get_vlan(port_index)
                     for chain_index in range(self.chain_count)]
         # no openstack
index 530ad69..b3e4a04 100644 (file)
@@ -21,6 +21,8 @@ import getpass
 from keystoneauth1.identity import v2
 from keystoneauth1.identity import v3
 from keystoneauth1 import session
+from keystoneclient import client
+from keystoneclient import utils
 from log import LOG
 
 
@@ -106,6 +108,7 @@ class Credentials(object):
         self.rc_project_domain_name = None
         self.rc_project_name = None
         self.rc_identity_api_version = 2
+        self.is_admin = False
         success = True
 
         if openrc_file:
@@ -164,3 +167,15 @@ class Credentials(object):
                     'Please enter your OpenStack Password: ')
         if not self.rc_password:
             self.rc_password = ""
+
+        # check if user has admin role in OpenStack project
+        try:
+            keystone = client.Client(session=self.get_session())
+            user = utils.find_resource(keystone.users, self.rc_username)
+            project = utils.find_resource(keystone.projects, self.rc_project_name)
+            roles = keystone.roles.list(user=user.id, project=project.id)
+            for role in roles:
+                if role.name == 'admin':
+                    self.is_admin = True
+        except Exception:
+            LOG.warning("User is not admin, no permission to list user roles")
index ebc606e..812aece 100644 (file)
@@ -22,6 +22,7 @@ from mock import patch
 import pytest
 
 from nfvbench.chain_runner import ChainRunner
+from nfvbench.chaining import ChainException
 from nfvbench.chaining import ChainVnfPort
 from nfvbench.chaining import InstancePlacer
 from nfvbench.compute import Compute
@@ -119,18 +120,87 @@ def _test_pvp_chain(config, cred, mock_glance, mock_neutron, mock_client):
     openstack_spec = OpenStackSpec()
     specs.set_openstack_spec(openstack_spec)
     cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = True
     runner = ChainRunner(config, cred, specs, BasicFactory())
     runner.close()
 
 def test_pvp_chain_runner():
     """Test PVP chain runner."""
     cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = True
     for shared_net in [True, False]:
         for sc in [ChainType.PVP]:
             for scc in [1, 2]:
                 config = _get_chain_config(sc, scc, shared_net)
                 _test_pvp_chain(config, cred)
 
+
+# Test not admin exception with empty value is raised
+@patch.object(Compute, 'find_image', _mock_find_image)
+@patch('nfvbench.chaining.Client')
+@patch('nfvbench.chaining.neutronclient')
+@patch('nfvbench.chaining.glanceclient')
+def _test_pvp_chain_no_admin_no_config_values(config, cred, mock_glance, mock_neutron, mock_client):
+    # instance = self.novaclient.servers.create(name=vmname,...)
+    # instance.status == 'ACTIVE'
+    mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
+    netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
+    mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
+    mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
+    specs = Specs()
+    openstack_spec = OpenStackSpec()
+    specs.set_openstack_spec(openstack_spec)
+    runner = ChainRunner(config, cred, specs, BasicFactory())
+    runner.close()
+
+def test_pvp_chain_runner_no_admin_no_config_values():
+    """Test PVP chain runner."""
+    cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = False
+    for shared_net in [True, False]:
+        for sc in [ChainType.PVP]:
+            for scc in [1, 2]:
+                config = _get_chain_config(sc, scc, shared_net)
+                with pytest.raises(ChainException):
+                    _test_pvp_chain_no_admin_no_config_values(config, cred)
+
+# Test not admin with mandatory parameters values in config file
+@patch.object(Compute, 'find_image', _mock_find_image)
+@patch('nfvbench.chaining.Client')
+@patch('nfvbench.chaining.neutronclient')
+@patch('nfvbench.chaining.glanceclient')
+def _test_pvp_chain_no_admin_config_values(config, cred, mock_glance, mock_neutron, mock_client):
+    # instance = self.novaclient.servers.create(name=vmname,...)
+    # instance.status == 'ACTIVE'
+    mock_client.return_value.servers.create.return_value.status = 'ACTIVE'
+    netw = {'id': 0, 'provider:network_type': 'vlan', 'provider:segmentation_id': 1000}
+    mock_neutron.Client.return_value.create_network.return_value = {'network': netw}
+    mock_neutron.Client.return_value.list_networks.return_value = {'networks': None}
+    specs = Specs()
+    openstack_spec = OpenStackSpec()
+    specs.set_openstack_spec(openstack_spec)
+    runner = ChainRunner(config, cred, specs, BasicFactory())
+    runner.close()
+
+def test_pvp_chain_runner_no_admin_config_values():
+    """Test PVP chain runner."""
+    cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = False
+    for shared_net in [True, False]:
+        for sc in [ChainType.PVP]:
+            for scc in [1, 2]:
+                config = _get_chain_config(sc, scc, shared_net)
+                config.availability_zone = "az"
+                config.hypervisor_hostname = "server"
+                # these are the 2 valid forms of vlan ranges
+                if scc == 1:
+                    config.vlans = [100, 200]
+                else:
+                    config.vlans = [[port * 100 + index for index in range(scc)]
+                                    for port in range(2)]
+                _test_pvp_chain_no_admin_config_values(config, cred)
+
+
 @patch.object(Compute, 'find_image', _mock_find_image)
 @patch('nfvbench.chaining.Client')
 @patch('nfvbench.chaining.neutronclient')
@@ -145,6 +215,7 @@ def _test_ext_chain(config, cred, mock_glance, mock_neutron, mock_client):
     openstack_spec = OpenStackSpec()
     specs.set_openstack_spec(openstack_spec)
     cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = True
     runner = ChainRunner(config, cred, specs, BasicFactory())
     runner.close()
 
@@ -155,6 +226,7 @@ def test_ext_chain_runner():
     shared/not shared net x arp/no_arp x scc 1 or 2
     """
     cred = MagicMock(spec=nfvbench.credentials.Credentials)
+    cred.is_admin = True
     for shared_net in [True, False]:
         for no_arp in [False, True]:
             for scc in [1, 2]: