From: Fran�ois-R�gis Menguy Date: Fri, 4 Jun 2021 13:48:46 +0000 (+0000) Subject: Merge "Increase Xtesting version in requirements and remove behaveframework temporary... X-Git-Tag: 5.0.3~4 X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=commitdiff_plain;h=6c9ada52712cbfb461f669cbdf29d31f44abb9a8;hp=7bee0560bcfdbdd9f92d9bd6d70c7ab1aa6a8325;p=nfvbench.git Merge "Increase Xtesting version in requirements and remove behaveframework temporary patch" --- diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml index 8e822e5..c76e738 100644 --- a/nfvbench/cfg.default.yaml +++ b/nfvbench/cfg.default.yaml @@ -30,8 +30,25 @@ # - availability_zone # - hypervisor_hostname # - vlans +# WARNING: Not used if clouds_detail is sets openrc_file: +# The OpenStack clouds configuration from clouds.yaml file to use. +# clouds.yaml file must be in one of the following paths: +# - ~/.config/openstack +# - /etc/openstack +# Note: If running in a container, this path must be valid in the container. +# 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 user is not admin some parameters are mandatory and must be filled with valid values in config file such as : +# - availability_zone +# - hypervisor_hostname +# - vlans +# If a value is sets, this parameter disable the use of openrc file +clouds_detail: + # Forwarder to use in nfvbenchvm image. Available options: ['vpp', 'testpmd'] vm_forwarder: testpmd diff --git a/nfvbench/cleanup.py b/nfvbench/cleanup.py index 23cdf56..cefdcfa 100644 --- a/nfvbench/cleanup.py +++ b/nfvbench/cleanup.py @@ -235,7 +235,7 @@ class Cleaner(object): """Cleaner for all NFVbench resources.""" def __init__(self, config): - cred = credentials.Credentials(config.openrc_file, None, False) + cred = credentials.Credentials(config.openrc_file, config.clouds_detail, None, False) session = cred.get_session() self.neutron_client = nclient.Client('2.0', session=session) self.nova_client = Client(2, session=session) diff --git a/nfvbench/credentials.py b/nfvbench/credentials.py index 4e4985f..a707ba3 100644 --- a/nfvbench/credentials.py +++ b/nfvbench/credentials.py @@ -21,32 +21,40 @@ import getpass from keystoneauth1.identity import v2 from keystoneauth1.identity import v3 from keystoneauth1 import session +import openstack +from keystoneclient.exceptions import HTTPClientError + from .log import LOG class Credentials(object): def get_session(self): - dct = { - 'username': self.rc_username, - 'password': self.rc_password, - 'auth_url': self.rc_auth_url - } - auth = None - - if self.rc_identity_api_version == 3: - dct.update({ - 'project_name': self.rc_project_name, - 'project_domain_name': self.rc_project_domain_name, - 'user_domain_name': self.rc_user_domain_name - }) - auth = v3.Password(**dct) + + if self.clouds_detail: + connection = openstack.connect(cloud=self.clouds_detail) + cred_session = connection.session else: - dct.update({ - 'tenant_name': self.rc_tenant_name - }) - auth = v2.Password(**dct) - return session.Session(auth=auth, verify=self.rc_cacert) + dct = { + 'username': self.rc_username, + 'password': self.rc_password, + 'auth_url': self.rc_auth_url + } + + if self.rc_identity_api_version == 3: + dct.update({ + 'project_name': self.rc_project_name, + 'project_domain_name': self.rc_project_domain_name, + 'user_domain_name': self.rc_user_domain_name + }) + auth = v3.Password(**dct) + else: + dct.update({ + 'tenant_name': self.rc_tenant_name + }) + auth = v2.Password(**dct) + cred_session = session.Session(auth=auth, verify=self.rc_cacert) + return cred_session def __parse_openrc(self, file): export_re = re.compile('export OS_([A-Z_]*)="?(.*)') @@ -91,11 +99,28 @@ class Credentials(object): elif name == "PROJECT_DOMAIN_NAME": self.rc_project_domain_name = value + # /users URL returns exception (HTTP 403) if user is not admin. + # try first without the version in case session already has it in + # Return HTTP 200 if user is admin + def __user_is_admin(self, url): + is_admin = False + try: + # check if user has admin role in OpenStack project + filter = {'service_type': 'identity', + 'interface': 'public'} + self.get_session().get(url, endpoint_filter=filter) + is_admin = True + except HTTPClientError as exc: + if exc.http_status == 403: + LOG.warning( + "User is not admin, no permission to list user roles. Exception: %s", exc) + return is_admin + # # Read a openrc file and take care of the password # The 2 args are passed from the command line and can be None # - def __init__(self, openrc_file, pwd=None, no_env=False): + def __init__(self, openrc_file, clouds_detail, pwd=None, no_env=False): self.rc_password = None self.rc_username = None self.rc_tenant_name = None @@ -105,8 +130,9 @@ class Credentials(object): self.rc_user_domain_name = None self.rc_project_domain_name = None self.rc_project_name = None - self.rc_identity_api_version = 2 + self.rc_identity_api_version = 3 self.is_admin = False + self.clouds_detail = clouds_detail success = True if openrc_file: @@ -118,7 +144,7 @@ class Credentials(object): success = False else: self.__parse_openrc(openrc_file) - elif not no_env: + elif not clouds_detail and not no_env: # no openrc file passed - we assume the variables have been # sourced by the calling shell # just check that they are present @@ -153,34 +179,27 @@ class Credentials(object): # always override with CLI argument if provided - if pwd: - self.rc_password = pwd - # if password not know, check from env variable - elif self.rc_auth_url and not self.rc_password and success: - if 'OS_PASSWORD' in os.environ and not no_env: - self.rc_password = os.environ['OS_PASSWORD'] - else: - # interactively ask for password - self.rc_password = getpass.getpass( - 'Please enter your OpenStack Password: ') - if not self.rc_password: - self.rc_password = "" - - # check if user has admin role in OpenStack project - filter = {'service_type': 'identity', - 'interface': 'public', - 'region_name': self.rc_region_name} + if not clouds_detail: + if pwd: + self.rc_password = pwd + # if password not know, check from env variable + elif self.rc_auth_url and not self.rc_password and success: + if 'OS_PASSWORD' in os.environ and not no_env: + self.rc_password = os.environ['OS_PASSWORD'] + else: + # interactively ask for password + self.rc_password = getpass.getpass( + 'Please enter your OpenStack Password: ') + if not self.rc_password: + self.rc_password = "" + + try: # /users URL returns exception (HTTP 403) if user is not admin. # try first without the version in case session already has it in # Return HTTP 200 if user is admin - self.get_session().get('/users', endpoint_filter=filter) - self.is_admin = True - except Exception: - try: - # vX/users URL returns exception (HTTP 403) if user is not admin. - self.get_session().get('/v' + str(self.rc_identity_api_version) + '/users', - endpoint_filter=filter) - self.is_admin = True - except Exception as e: - LOG.warning("User is not admin, no permission to list user roles. Exception: %s", e) + self.is_admin = self.__user_is_admin('/users') or self.__user_is_admin( + '/v2/users') or self.__user_is_admin('/v3/users') + except Exception as e: + LOG.warning("Error occurred during Openstack API access. " + "Unable to check user is admin. Exception: %s", e) diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py index 598247a..0719247 100644 --- a/nfvbench/nfvbench.py +++ b/nfvbench/nfvbench.py @@ -60,8 +60,8 @@ class NFVBench(object): self.config_plugin = config_plugin self.factory = factory self.notifier = notifier - self.cred = credentials.Credentials(config.openrc_file, None, False) \ - if config.openrc_file else None + self.cred = credentials.Credentials(config.openrc_file, config.clouds_detail, None, False) \ + if config.openrc_file or config.clouds_detail else None self.chain_runner = None self.specs = Specs() self.specs.set_openstack_spec(openstack_spec) @@ -96,8 +96,10 @@ class NFVBench(object): # check that an empty openrc file (no OpenStack) is only allowed # with EXT chain - if not self.config.openrc_file and self.config.service_chain != ChainType.EXT: - raise Exception("openrc_file in the configuration is required for PVP/PVVP chains") + if (not self.config.openrc_file and not self.config.clouds_detail) and \ + self.config.service_chain != ChainType.EXT: + raise Exception("openrc_file or clouds_detail in the configuration is required" + " for PVP/PVVP chains") self.specs.set_run_spec(self.config_plugin.get_run_spec(self.config, self.specs.openstack)) diff --git a/nfvbenchvm/README.rst b/nfvbenchvm/README.rst index 0274c75..dad9f46 100644 --- a/nfvbenchvm/README.rst +++ b/nfvbenchvm/README.rst @@ -95,6 +95,29 @@ Hardcoded Username and Password GENERATOR IMAGE INSTANCE AND CONFIG =================================== +Pre-requisites +-------------- +To use openstack APIs, NFVbench generator VM will use `clouds.yaml` file as openstack configuration. +The OpenStack clouds configuration from clouds.yaml file to use. +clouds.yaml file must be in one of the following paths: +- ~/.config/openstack +- /etc/openstack + +Example of `clouds.yaml`: + +.. code-block:: yaml + + clouds: + devstack: + auth: + auth_url: http://192.168.122.10:35357/ + project_name: demo + username: demo + password: 0penstack + region_name: RegionOne + +.. note:: Add `CLOUD_DETAIL` property with the accurate value for your openstack configuration (`devstack` in the above example) in ``/etc/nfvbenchvm.conf`` + Interface Requirements ---------------------- The instance must be launched using OpenStack with 2 network interfaces for dataplane traffic (using SR-IOV function) and 1 management interface to control nfvbench. @@ -135,6 +158,7 @@ Template of a genarator profile using CPU pinning: pci: "{{PCI_ADDRESS_2}}" switch: intf_speed: + .. note:: `CORE_THREADS` value is determined automatically based on the cores available on the VM starting from 2 to last worker core available. Auto-configuration @@ -155,6 +179,7 @@ Example of configuration: LOOPBACK_INTF_MAC2=FA:16:3E:10:DA:10 E2E_INTF_MAC1=FA:16:3E:B0:E2:43 E2E_INTF_MAC2=FA:16:3E:D3:6A:FC + .. note:: `ACTION` parameter is not mandatory but will permit to start NFVbench with the accurate ports (loopback or e2e). .. note:: Set of MAC parameters cannot be used in parallel as only one NFVbench/TRex process is running. .. note:: Switching from `loopback` to `e2e` action can be done manually using `/nfvbench/start-nfvbench.sh ` with the accurate keyword for `action` parameter. This script will restart NFVbench with the good set of MAC. @@ -185,6 +210,7 @@ Using pre-created direct-physical ports on openstack, mac addresses value are on INTF_MGMT_CIDR=172.20.56.228/2 INTF_MGMT_IP_GW=172.20.56.225 DNS_SERVERS=8.8.8.8,dns.server.com + .. note:: A management interface is required to automatically find the virtual interface to use according to the MAC address provided (see `INTF_MAC_MGMT` parameter). .. note:: NFVbench VM will call openstack API through the management interface to retrieve mac address for these ports .. note:: If openstack API required a host name resolution, add the parameter DNS_SERVERS to add IP or DNS server names (multiple servers can be added separated by a `,`) diff --git a/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.generator b/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.generator index 0746fd6..633403c 100644 --- a/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.generator +++ b/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.generator @@ -4,7 +4,6 @@ touch /var/lock/subsys/local # Waiting for cloud-init to generate $NFVBENCH_CONF, retry 60 seconds NFVBENCH_CONF=/etc/nfvbenchvm.conf -OPENRC=/etc/nfvbench/openrc retry=30 until [ $retry -eq 0 ]; do if [ -f $NFVBENCH_CONF ]; then break; fi @@ -20,10 +19,6 @@ echo "Generating configurations for NFVbench and TRex..." eval $(cat $NFVBENCH_CONF) touch /nfvbench_configured.flag -if [ -f $OPENRC ]; then - source $OPENRC -fi - # Add DNS entry if [ $DNS_SERVERS ]; then IFS="," read -a dns <<< $DNS_SERVERS @@ -80,8 +75,8 @@ get_eth_port() { if [ $INTF_MGMT_CIDR ] && [ $INTF_MGMT_IP_GW ]; then if [ $INTF_MAC_MGMT ]; then ETH_PORT=$(get_eth_port $INTF_MAC_MGMT) - elif [ -f $OPENRC ] && [ "$PORT_MGMT_NAME" ]; then - $INTF_MAC_MGMT=$(openstack port list | grep $PORT_MGMT_NAME | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + elif [ "$CLOUD_DETAIL" ] && [ "$PORT_MGMT_NAME" ]; then + $INTF_MAC_MGMT=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $PORT_MGMT_NAME | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) ETH_PORT=$(get_eth_port $INTF_MAC_MGMT) else ETH_PORT="" diff --git a/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.loopvm b/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.loopvm index bc14902..ecc8b05 100644 --- a/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.loopvm +++ b/nfvbenchvm/dib/elements/nfvbenchvm/static/etc/rc.d/rc.local.loopvm @@ -167,10 +167,11 @@ fi if [ $PCI_ADDRESS_1 ] && [ $PCI_ADDRESS_2 ]; then logger "NFVBENCHVM: Using pci $PCI_ADDRESS_1 ($INTF_MAC1)" logger "NFVBENCHVM: Using pci $PCI_ADDRESS_2 ($INTF_MAC2)" + # active uio_pci_generic driver + modprobe uio_pci_generic # Configure the forwarder if [ "$FORWARDER" == "testpmd" ]; then echo "Configuring testpmd..." - modprobe uio_pci_generic mkdir /dpdk echo "set promisc all off" > /dpdk/testpmd_cmd.txt # Binding ports to DPDK VFIO or UIO diff --git a/nfvbenchvm/dib/elements/nfvbenchvm/static/nfvbench/configure-nfvbench.sh b/nfvbenchvm/dib/elements/nfvbenchvm/static/nfvbench/configure-nfvbench.sh index 5ec584b..3bf1d8d 100644 --- a/nfvbenchvm/dib/elements/nfvbenchvm/static/nfvbench/configure-nfvbench.sh +++ b/nfvbenchvm/dib/elements/nfvbenchvm/static/nfvbench/configure-nfvbench.sh @@ -6,15 +6,10 @@ NFVBENCH_CONF=/etc/nfvbenchvm.conf E2E_CFG=/etc/nfvbench/e2e.cfg LOOPBACK_CFG=/etc/nfvbench/loopback.cfg NFVBENCH_CFG=/etc/nfvbench/nfvbench.cfg -OPENRC=/etc/nfvbench/openrc # Parse and obtain all configurations eval $(cat $NFVBENCH_CONF) -if [ -f $OPENRC ]; then - source $OPENRC -fi - # WE assume there are at least 2 cores available for the VM CPU_CORES=$(grep -c ^processor /proc/cpuinfo) @@ -88,21 +83,21 @@ get_interfaces_mac_values(){ # Set dynamically interfaces mac values, if VM is spawn with SRIOV PF ports # and openstack API are accessible if [ -z "$LOOPBACK_INTF_MAC1" ] && [ -z "$LOOPBACK_INTF_MAC2" ]; then - if [ -f $OPENRC ] && [ "$LOOPBACK_PORT_NAME1" ] && [ "$LOOPBACK_PORT_NAME2" ]; then - LOOPBACK_INTF_MAC1=$(openstack port list | grep $LOOPBACK_PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) - LOOPBACK_INTF_MAC2=$(openstack port list | grep $LOOPBACK_PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + if [ "$CLOUD_DETAIL" ] && [ "$LOOPBACK_PORT_NAME1" ] && [ "$LOOPBACK_PORT_NAME2" ]; then + LOOPBACK_INTF_MAC1=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $LOOPBACK_PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + LOOPBACK_INTF_MAC2=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $LOOPBACK_PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) fi fi if [ -z "$E2E_INTF_MAC1" ] && [ -z "$E2E_INTF_MAC2" ]; then - if [ -f $OPENRC ] && [ "$E2E_PORT_NAME1" ] && [ "$E2E_PORT_NAME2" ]; then - E2E_INTF_MAC1=$(openstack port list | grep $E2E_PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) - E2E_INTF_MAC2=$(openstack port list | grep $E2E_PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + if [ "$CLOUD_DETAIL" ] && [ "$E2E_PORT_NAME1" ] && [ "$E2E_PORT_NAME2" ]; then + E2E_INTF_MAC1=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $E2E_PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + E2E_INTF_MAC2=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $E2E_PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) fi fi if [ -z "$INTF_MAC1" ] && [ -z "$INTF_MAC2" ]; then - if [ -f $OPENRC ] && [ "$PORT_NAME1" ] && [ "$PORT_NAME2" ]; then - INTF_MAC1=$(openstack port list | grep $PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) - INTF_MAC2=$(openstack port list | grep $PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + if [ "$CLOUD_DETAIL" ] && [ "$PORT_NAME1" ] && [ "$PORT_NAME2" ]; then + INTF_MAC1=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $PORT_NAME1 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) + INTF_MAC2=$(openstack --os-cloud $CLOUD_DETAIL port list | grep $PORT_NAME2 | grep -o -Ei '([a-fA-F0-9:]{17}|[a-fA-F0-9]{12}$)' | head -1) fi fi } diff --git a/nfvbenchvm/dib/elements/nfvbenchvm/static/vpp/startup.conf b/nfvbenchvm/dib/elements/nfvbenchvm/static/vpp/startup.conf index d174299..874f6cb 100644 --- a/nfvbenchvm/dib/elements/nfvbenchvm/static/vpp/startup.conf +++ b/nfvbenchvm/dib/elements/nfvbenchvm/static/vpp/startup.conf @@ -19,7 +19,7 @@ dpdk { socket-mem 1024 dev {{PCI_ADDRESS_1}} dev {{PCI_ADDRESS_2}} - uio-driver igb_uio + uio-driver uio_pci_generic num-mbufs {{NUM_MBUFS}} } diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py index e53a586..360e3bd 100644 --- a/test/test_nfvbench.py +++ b/test/test_nfvbench.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. # +import openstack +from keystoneauth1.exceptions import HTTPClientError from mock import patch import pytest @@ -139,13 +141,41 @@ def test_load_from_rate(): # ========================================================================= def test_no_credentials(): - cred = Credentials('/completely/wrong/path/openrc', None, False) - if cred.rc_auth_url: - # shouldn't get valid data unless user set environment variables + with patch.object(openstack, 'connect') as mock: + cred = Credentials('/completely/wrong/path/openrc', None, None, False) + if cred.rc_auth_url: + # shouldn't get valid data unless user set environment variables + assert False + else: + assert True + mock.assert_not_called() + + +def test_clouds_file_credentials(): + with patch.object(openstack, 'connect') as mock: + Credentials(None, 'openstack', None, False) + mock.assert_called_once() + + +@patch('nfvbench.nfvbench.credentials') +def test_is_not_admin(mock_session): + mock_session.Session.return_value.get.return_value.raiseError.side_effect = HTTPClientError + cred = Credentials(None, 'openstack', None, False) + if cred.is_admin: assert False else: assert True + +def test_is_admin(): + with patch.object(openstack, 'connect'): + cred = Credentials(None, 'openstack', None, False) + if cred.is_admin: + assert True + else: + assert False + + def test_ip_block(): ipb = IpBlock('10.0.0.0', '0.0.0.1', 256) assert ipb.get_ip() == '10.0.0.0'