Fix physical host reservation for non-admin users
Originally, Blazar was using its service user to manage objects for physical host reservation, e.g. host aggregates, which by default requires admin rights. Commit 16d5f67ba7020701edbbf09a747f5683b0840c21 started using a dedicated account configured with values climate_username, climate_password, and climate_tenant_name. Commit c9b7307cf3c97d3b48878aca6eda5b7fbc4dcfa7 removed this dedicated account and started using trusts instead, so that operations were performed on behalf of the user creating the lease (with the trustee being the blazar service user). While this works well if users creating leases are admins, non-admin users will get errors because the default Nova policy prevents them from running required operations associated with aggregates and hypervisors. Since it is not clear why a dedicated account for admin operations was required, this patch brings back the approach used before commit 16d5f67ba7020701edbbf09a747f5683b0840c21, which was to use the service account for admin operations. This allows non-admin users to create Blazar leases. The nova client setup is updated to authenticate against Keystone v3. Change-Id: Iad86bb549aec13edd662965d2f91b68c856ae06c Closes-Bug: #1663204
This commit is contained in:
parent
124ac0557a
commit
3de6f73e92
@ -9,8 +9,6 @@ OpenStack Reservation Service
|
||||
Prerequisites
|
||||
-------------
|
||||
* Keystone v3 API endpoint
|
||||
* Dedicated account for write operations on behalf of the admin
|
||||
blazar_username
|
||||
* Service account
|
||||
|
||||
Configuration
|
||||
|
@ -58,6 +58,12 @@ os_opts = [
|
||||
cfg.StrOpt('os_auth_version',
|
||||
default='v2.0',
|
||||
help='Blazar uses API v3 to allow trusts using.'),
|
||||
cfg.StrOpt('os_admin_user_domain_name',
|
||||
default='Default',
|
||||
help='A domain name the os_admin_username belongs to.'),
|
||||
cfg.StrOpt('os_admin_project_domain_name',
|
||||
default='Default',
|
||||
help='A domain name the os_admin_project_name belongs to')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -58,7 +58,12 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
|
||||
pool = None
|
||||
|
||||
def __init__(self):
|
||||
super(PhysicalHostPlugin, self).__init__()
|
||||
super(PhysicalHostPlugin, self).__init__(
|
||||
username=CONF.os_admin_username,
|
||||
password=CONF.os_admin_password,
|
||||
user_domain_name=CONF.os_admin_user_domain_name,
|
||||
project_name=CONF.os_admin_project_name,
|
||||
project_domain_name=CONF.os_admin_user_domain_name)
|
||||
|
||||
def create_reservation(self, values):
|
||||
"""Create reservation."""
|
||||
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from oslo_config import cfg
|
||||
|
||||
from blazar.manager import exceptions as manager_exceptions
|
||||
from blazar.utils.openstack import nova
|
||||
@ -21,7 +22,12 @@ from blazar.utils.openstack import nova
|
||||
|
||||
class NovaInventory(nova.NovaClientWrapper):
|
||||
def __init__(self):
|
||||
super(NovaInventory, self).__init__()
|
||||
super(NovaInventory, self).__init__(
|
||||
username=cfg.CONF.os_admin_username,
|
||||
password=cfg.CONF.os_admin_password,
|
||||
user_domain_name=cfg.CONF.os_admin_user_domain_name,
|
||||
project_name=cfg.CONF.os_admin_project_name,
|
||||
project_domain_name=cfg.CONF.os_admin_user_domain_name)
|
||||
|
||||
def get_host_details(self, host):
|
||||
"""Get Nova capabilities of a single host
|
||||
|
@ -52,7 +52,12 @@ CONF.register_opts(OPTS, group=plugin.RESOURCE_TYPE)
|
||||
|
||||
class ReservationPool(nova.NovaClientWrapper):
|
||||
def __init__(self):
|
||||
super(ReservationPool, self).__init__()
|
||||
super(ReservationPool, self).__init__(
|
||||
username=CONF.os_admin_username,
|
||||
password=CONF.os_admin_password,
|
||||
user_domain_name=CONF.os_admin_user_domain_name,
|
||||
project_name=CONF.os_admin_project_name,
|
||||
project_domain_name=CONF.os_admin_user_domain_name)
|
||||
self.config = CONF[plugin.RESOURCE_TYPE]
|
||||
self.freepool_name = self.config.aggregate_freepool_name
|
||||
|
||||
|
@ -16,12 +16,15 @@
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1 import token_endpoint
|
||||
from novaclient import client as nova_client
|
||||
from oslo_config import cfg
|
||||
|
||||
from blazar import context
|
||||
from blazar import tests
|
||||
from blazar.utils.openstack import base
|
||||
from blazar.utils.openstack import nova
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestCNClient(tests.TestCase):
|
||||
def setUp(self):
|
||||
@ -43,20 +46,34 @@ class TestCNClient(tests.TestCase):
|
||||
|
||||
def test_client_from_kwargs(self):
|
||||
self.ctx.side_effect = RuntimeError
|
||||
self.auth_token = 'fake_token'
|
||||
self.endpoint = 'fake_endpoint'
|
||||
endpoint = 'fake_endpoint'
|
||||
username = 'blazar_admin'
|
||||
password = 'blazar_password'
|
||||
user_domain = 'User_Domain'
|
||||
project_name = 'admin'
|
||||
project_domain = 'Project_Domain'
|
||||
auth_url = "%s://%s:%s/v3" % (CONF.os_auth_protocol,
|
||||
CONF.os_auth_host,
|
||||
CONF.os_auth_port)
|
||||
|
||||
kwargs = {'version': self.version,
|
||||
'endpoint_override': self.endpoint,
|
||||
'auth_token': self.auth_token}
|
||||
'endpoint_override': endpoint,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'user_domain_name': user_domain,
|
||||
'project_name': project_name,
|
||||
'project_domain_name': project_domain}
|
||||
|
||||
self.nova.BlazarNovaClient(**kwargs)
|
||||
|
||||
self.auth.assert_called_once_with(self.endpoint, self.auth_token)
|
||||
self.session.assert_called_once_with(auth=self.auth.return_value)
|
||||
self.client.assert_called_once_with(version=self.version,
|
||||
endpoint_override=self.endpoint,
|
||||
session=self.session.return_value)
|
||||
username=username,
|
||||
password=password,
|
||||
user_domain_name=user_domain,
|
||||
project_name=project_name,
|
||||
project_domain_name=project_domain,
|
||||
auth_url=auth_url,
|
||||
endpoint_override=endpoint)
|
||||
|
||||
def test_client_from_ctx(self):
|
||||
kwargs = {'version': self.version}
|
||||
|
@ -45,24 +45,49 @@ class BlazarNovaClient(object):
|
||||
def __init__(self, **kwargs):
|
||||
"""Description
|
||||
|
||||
We suppose that in future we may want to use CNC in some places
|
||||
where context will be available, so we create 2 different ways of
|
||||
creating client from context(future) and kwargs(we use it now).
|
||||
BlazarNovaClient can be used in two ways: from context or kwargs.
|
||||
|
||||
:param version: service client version which we will use
|
||||
:type version: str
|
||||
|
||||
:param ctx: request context
|
||||
:type ctx: context object
|
||||
|
||||
:param auth_token: keystone auth token
|
||||
:type auth_token: str
|
||||
|
||||
:param endpoint_override: endpoint url which we will use
|
||||
:type endpoint_override: str
|
||||
|
||||
:param username: username to use with nova client
|
||||
:type username: str
|
||||
|
||||
:param password: password to use with nova client
|
||||
:type password: str
|
||||
|
||||
:param user_domain_name: domain name of the user
|
||||
:type user_domain_name: str
|
||||
|
||||
:param project_name: project name to use with nova client
|
||||
:type project_name: str
|
||||
|
||||
:param project_domain_name: domain name of the project
|
||||
:type project_domain_name: str
|
||||
|
||||
:param auth_url: keystone url to authenticate against
|
||||
:type auth_url: str
|
||||
"""
|
||||
|
||||
ctx = kwargs.pop('ctx', None)
|
||||
auth_token = kwargs.pop('auth_token', None)
|
||||
endpoint_override = kwargs.pop('endpoint_override', None)
|
||||
version = kwargs.pop('version', cfg.CONF.nova_client_version)
|
||||
username = kwargs.pop('username', None)
|
||||
password = kwargs.pop('password', None)
|
||||
user_domain_name = kwargs.pop('user_domain_name', None)
|
||||
project_name = kwargs.pop('project_name', None)
|
||||
project_domain_name = kwargs.pop('project_domain_name', None)
|
||||
auth_url = kwargs.pop('auth_url', None)
|
||||
|
||||
if ctx is None:
|
||||
try:
|
||||
@ -74,13 +99,29 @@ class BlazarNovaClient(object):
|
||||
endpoint_override = endpoint_override or \
|
||||
base.url_for(ctx.service_catalog,
|
||||
CONF.compute_service)
|
||||
auth_url = base.url_for(ctx.service_catalog, CONF.identity_service)
|
||||
|
||||
auth = token_endpoint.Token(endpoint_override,
|
||||
auth_token)
|
||||
sess = session.Session(auth=auth)
|
||||
if auth_url is None:
|
||||
auth_url = "%s://%s:%s/v3" % (CONF.os_auth_protocol,
|
||||
CONF.os_auth_host,
|
||||
CONF.os_auth_port)
|
||||
|
||||
if username:
|
||||
kwargs.setdefault('username', username)
|
||||
kwargs.setdefault('password', password)
|
||||
kwargs.setdefault('project_name', project_name)
|
||||
kwargs.setdefault('auth_url', auth_url)
|
||||
|
||||
if "v2.0" not in auth_url:
|
||||
kwargs.setdefault('project_domain_name', project_domain_name)
|
||||
kwargs.setdefault('user_domain_name', user_domain_name)
|
||||
else:
|
||||
auth = token_endpoint.Token(endpoint_override,
|
||||
auth_token)
|
||||
sess = session.Session(auth=auth)
|
||||
kwargs.setdefault('session', sess)
|
||||
|
||||
kwargs.setdefault('endpoint_override', endpoint_override)
|
||||
kwargs.setdefault('session', sess)
|
||||
kwargs.setdefault('version', version)
|
||||
self.nova = nova_client.Client(**kwargs)
|
||||
|
||||
@ -117,8 +158,21 @@ class ServerManager(servers.ServerManager):
|
||||
|
||||
|
||||
class NovaClientWrapper(object):
|
||||
def __init__(self, username=None, password=None, user_domain_name=None,
|
||||
project_name=None, project_domain_name=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.user_domain_name = user_domain_name
|
||||
self.project_name = project_name
|
||||
self.project_domain_name = project_domain_name
|
||||
|
||||
@property
|
||||
def nova(self):
|
||||
ctx = context.current()
|
||||
nova = BlazarNovaClient(ctx=ctx)
|
||||
nova = BlazarNovaClient(ctx=ctx,
|
||||
username=self.username,
|
||||
password=self.password,
|
||||
user_domain_name=self.user_domain_name,
|
||||
project_name=self.project_name,
|
||||
project_domain_name=self.project_domain_name)
|
||||
return nova
|
||||
|
@ -54,9 +54,6 @@ function configure_blazar {
|
||||
# keystone authtoken
|
||||
_blazar_setup_keystone $BLAZAR_CONF_FILE keystone_authtoken
|
||||
|
||||
iniset $BLAZAR_CONF_FILE physical:host blazar_username $BLAZAR_USER_NAME
|
||||
iniset $BLAZAR_CONF_FILE physical:host blazar_password $SERVICE_PASSWORD
|
||||
iniset $BLAZAR_CONF_FILE physical:host blazar_project_name $SERVICE_TENANT_NAME
|
||||
iniset $BLAZAR_CONF_FILE physical:host aggregate_freepool_name $BLAZAR_FREEPOOL_NAME
|
||||
|
||||
iniset $BLAZAR_CONF_FILE DEFAULT host $HOST_IP
|
||||
|
@ -139,9 +139,8 @@ Then edit */etc/blazar/blazar.conf* using the following example:
|
||||
|
||||
..
|
||||
|
||||
Here *os_admin_** flags refer to the Blazar service user. *blazar_** ones - to
|
||||
an admin user created specially to work with physical reservations. If you do
|
||||
not have these users, create them:
|
||||
*os_admin_** flags refer to the Blazar service user. If you do not have this
|
||||
user, create it:
|
||||
|
||||
.. sourcecode:: console
|
||||
|
||||
@ -150,9 +149,6 @@ not have these users, create them:
|
||||
|
||||
..
|
||||
|
||||
And the same procedure for special admin user to work with physical
|
||||
reservations.
|
||||
|
||||
Next you need to configure Nova. If you want to use physical reservations,
|
||||
please add the following lines to nova.conf file:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user