summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfmenguy <francoisregis.menguy@orange.com>2021-06-04 14:21:29 +0200
committerfmenguy <francoisregis.menguy@orange.com>2021-06-04 14:21:29 +0200
commit1ab93ad2bba7ca570d72c4823321169b9f235cf8 (patch)
treeb7b17758fa07607bf647cae478a4a3d841cc5c51
parentd181b23c2fc078faac0342de6102c62fb8a4f1f7 (diff)
NFVBENCH-212 Add clouds.yaml file as a config file to use for openstack API access
Change-Id: If855ffda1070ed9c9c4544230e4efec185a93f45 Signed-off-by: fmenguy <francoisregis.menguy@orange.com>
-rw-r--r--nfvbench/cfg.default.yaml17
-rw-r--r--nfvbench/cleanup.py2
-rw-r--r--nfvbench/credentials.py117
-rw-r--r--nfvbench/nfvbench.py10
-rw-r--r--test/test_nfvbench.py36
5 files changed, 125 insertions, 57 deletions
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/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'