Add support for PROJECT_DOMAIN_ID, USER_DOMAIN_ID.

Currently in kingbird-client there is NO support for
PROJECT_DOMAIN_NAME/USER_DOMAIN_NAME OR PROJECT_DOMAIN_ID/USER_DOMAIN_ID
but in the latest release of OpenStack,
there are options for the above-mentioned environment variables.
Added tests for the same and made README.rst better.
Closes-Bug: #1716364

Change-Id: I09625acafc0e3d43fd6008d3de930e98195d14f3
This commit is contained in:
GouthamPratapa 2017-09-11 15:24:53 +05:30
parent bf641ae368
commit 3c6a972c86
6 changed files with 182 additions and 41 deletions

View File

@ -1,11 +1,21 @@
Kingbird
=========
.. image:: https://img.shields.io/pypi/v/python-kingbirdclient.svg
:target: https://pypi.python.org/pypi/python-kingbirdclient/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/dm/python-kingbirdclient.svg
:target: https://pypi.python.org/pypi/python-kingbirdclient/
:alt: Downloads
Centralised service for multi-region OpenStack deployments.
Kingbird is an centralized OpenStack service that provides resource operation and
management across multiple OpenStack instances in a multi-region OpenStack deployment.
This service is part of the OPNFV Multisite project that intends to address
the use cases related to distributed cloud environments.
Kingbird provides features like centralized quota management, centralized view for
distributed virtual resources, global view for tenant level IP/MAC address space management,
synchronisation of ssh keys, images, flavors, etc. across regions.
@ -23,16 +33,16 @@ provides a Python API (the ``kingbirdclient`` module) and a command-line tool
Installation
------------
First of all, clone the repo and go to the repo directory:
First of all, clone the repo and go to the repo directory::
$ git clone https://github.com/openstack/python-kingbirdclient.git
$ cd python-kingbirdclient
Then just run:
Then just run::
$ pip install -e .
or
or::
$ pip install -r requirements.txt
$ python setup.py install
@ -41,25 +51,32 @@ Running Kingbird client
-----------------------
If Kingbird authentication is enabled, provide the information about OpenStack
auth to environment variables. Type:
auth to environment variables. Type::
$ export OS_PROJECT_DOMAIN_ID=default
$ export OS_REGION_NAME=RegionOne
$ export OS_USER_DOMAIN_ID=default
$ export OS_PROJECT_NAME=<project_name>
$ export OS_IDENTITY_API_VERSION=<identity_version>
$ export OS_PASSWORD=<password>
$ export OS_AUTH_TYPE=password
$ export OS_AUTH_URL=http://<Keystone_host>/identity
$ export OS_USERNAME=<user_name>
$ export OS_TENANT_NAME=<tenant_name>
$ export OS_VOLUME_API_VERSION=<volume_version>
$ export OS_PROJECT_DOMAIN_ID=<PROJECT_DOMAIN_ID>
$ export OS_REGION_NAME=<Region>
$ export OS_USER_DOMAIN_ID=<USER_DOMAIN_ID>
$ export OS_PROJECT_NAME=<project_name>
$ export OS_IDENTITY_API_VERSION=<identity_version>
$ export OS_PASSWORD=<password>
$ export OS_AUTH_TYPE=<auth_type>
$ export OS_AUTH_URL=http://<Keystone_host>/identity
$ export OS_USERNAME=<user_name>
$ export OS_TENANT_NAME=<tenant_name>
To make sure Kingbird client works, type:
.. note:: In client, we use Keystone auth version v3 as
server supports only v3.*
To make sure Kingbird client works, type::
$ kingbird quota defaults
You can see the list of available commands typing:
or::
$ kingbird sync list
You can see the list of available commands typing::
$ kingbird --help

View File

@ -22,7 +22,7 @@ def client(kingbird_url=None, username=None, api_key=None,
endpoint_type='publicURL', service_type='synchronization',
auth_token=None, user_id=None, cacert=None, insecure=False,
profile=None, auth_type='keystone', client_id=None,
client_secret=None):
client_secret=None, **kwargs):
if kingbird_url and not isinstance(kingbird_url, six.string_types):
raise RuntimeError('Kingbird url should be a string.')
@ -42,7 +42,8 @@ def client(kingbird_url=None, username=None, api_key=None,
profile=profile,
auth_type=auth_type,
client_id=client_id,
client_secret=client_secret
client_secret=client_secret,
**kwargs
)

View File

@ -38,7 +38,7 @@ class Client(object):
endpoint_type='publicURL', service_type='synchronization',
auth_token=None, user_id=None, cacert=None, insecure=False,
profile=None, auth_type='keystone', client_id=None,
client_secret=None, session=None):
client_secret=None, session=None, **kwargs):
"""Kingbird communicates with Keystone to fetch necessary values."""
if kingbird_url and not isinstance(kingbird_url, six.string_types):
raise RuntimeError('Kingbird url should be a string.')
@ -59,7 +59,8 @@ class Client(object):
user_id,
session,
cacert,
insecure
insecure,
**kwargs
)
)
else:
@ -93,7 +94,7 @@ def authenticate(kingbird_url=None, username=None,
api_key=None, project_name=None, auth_url=None,
project_id=None, endpoint_type='publicURL',
service_type='synchronization', auth_token=None, user_id=None,
session=None, cacert=None, insecure=False):
session=None, cacert=None, insecure=False, **kwargs):
"""Get token, project_id, user_id and Endpoint."""
if project_name and project_id:
raise RuntimeError(
@ -104,6 +105,10 @@ def authenticate(kingbird_url=None, username=None,
raise RuntimeError(
'Only user name or user id should be set'
)
user_domain_name = kwargs['user_domain_name']
user_domain_id = kwargs['user_domain_id']
project_domain_name = kwargs['project_domain_name']
project_domain_id = kwargs['project_domain_id']
if session is None:
if auth_token:
@ -111,7 +116,11 @@ def authenticate(kingbird_url=None, username=None,
auth_url=auth_url,
token=auth_token,
project_id=project_id,
project_name=project_name)
project_name=project_name,
project_domain_name=project_domain_name,
project_domain_id=project_domain_id,
cacert=cacert,
insecure=insecure)
elif api_key and (username or user_id):
auth = auth_plugin.Password(
@ -120,7 +129,11 @@ def authenticate(kingbird_url=None, username=None,
user_id=user_id,
password=api_key,
project_id=project_id,
project_name=project_name)
project_name=project_name,
user_domain_name=user_domain_name,
user_domain_id=user_domain_id,
project_domain_name=project_domain_name,
project_domain_id=project_domain_id)
else:
raise RuntimeError('You must either provide a valid token or'

View File

@ -247,18 +247,38 @@ class KingbirdShell(app.App):
'--os-tenant-id',
action='store',
dest='tenant_id',
default=c.env('OS_TENANT_ID'),
default=c.env('OS_TENANT_ID', 'OS_PROJECT_ID'),
help='Authentication tenant identifier (Env: OS_TENANT_ID)'
)
parser.add_argument(
'--os-project-id',
action='store',
dest='project_id',
default=c.env('OS_TENANT_ID', 'OS_PROJECT_ID'),
help='Authentication project identifier (Env: OS_TENANT_ID'
' or OS_PROJECT_ID), will use tenant_id if both tenant_id'
' and project_id are set'
)
parser.add_argument(
'--os-tenant-name',
action='store',
dest='tenant_name',
default=c.env('OS_TENANT_NAME', 'Default'),
default=c.env('OS_TENANT_NAME', 'OS_PROJECT_NAME'),
help='Authentication tenant name (Env: OS_TENANT_NAME)'
)
parser.add_argument(
'--os-project-name',
action='store',
dest='project_name',
default=c.env('OS_TENANT_NAME', 'OS_PROJECT_NAME'),
help='Authentication project name (Env: OS_TENANT_NAME'
' or OS_PROJECT_NAME), will use tenant_name if both'
' tenant_name and project_name are set'
)
parser.add_argument(
'--os-auth-token',
action='store',
@ -267,6 +287,42 @@ class KingbirdShell(app.App):
help='Authentication token (Env: OS_AUTH_TOKEN)'
)
parser.add_argument(
'--os-project-domain-name',
action='store',
dest='project_domain_name',
default=c.env('OS_PROJECT_DOMAIN_NAME'),
help='Authentication project domain name or ID'
' (Env: OS_PROJECT_DOMAIN_NAME)'
)
parser.add_argument(
'--os-project-domain-id',
action='store',
dest='project_domain_id',
default=c.env('OS_PROJECT_DOMAIN_ID'),
help='Authentication project domain ID'
' (Env: OS_PROJECT_DOMAIN_ID)'
)
parser.add_argument(
'--os-user-domain-name',
action='store',
dest='user_domain_name',
default=c.env('OS_USER_DOMAIN_NAME'),
help='Authentication user domain name'
' (Env: OS_USER_DOMAIN_NAME)'
)
parser.add_argument(
'--os-user-domain-id',
action='store',
dest='user_domain_id',
default=c.env('OS_USER_DOMAIN_ID'),
help='Authentication user domain name'
' (Env: OS_USER_DOMAIN_ID)'
)
parser.add_argument(
'--os-auth-url',
action='store',
@ -341,11 +397,18 @@ class KingbirdShell(app.App):
"via --os-password env[OS_PASSWORD]")
)
kwargs = {
'user_domain_name': self.options.user_domain_name,
'user_domain_id': self.options.user_domain_id,
'project_domain_name': self.options.project_domain_name,
'project_domain_id': self.options.project_domain_id
}
self.client = client.client(
kingbird_url=self.options.kingbird_url,
username=self.options.username,
api_key=self.options.password,
project_name=self.options.tenant_name,
project_name=self.options.tenant_name or self.options.project_name,
auth_url=self.options.auth_url,
project_id=self.options.tenant_id,
endpoint_type=self.options.endpoint_type,
@ -353,7 +416,8 @@ class KingbirdShell(app.App):
auth_token=self.options.token,
cacert=self.options.cacert,
insecure=self.options.insecure,
profile=self.options.profile
profile=self.options.profile,
**kwargs
)
if not self.options.auth_url and not skip_auth:

View File

@ -30,6 +30,10 @@ AUTH_HTTPS_URL = AUTH_HTTP_URL.replace('http', 'https')
KINGBIRD_HTTP_URL = 'http://localhost:8118/v1.0'
KINGBIRD_HTTPS_URL = KINGBIRD_HTTP_URL.replace('http', 'https')
PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
FAKE_KWARGS = {'user_domain_name': 'fake_user_domain_name',
'user_domain_id': 'fake_user_domain_id',
'project_domain_name': 'fake_project_domain_name',
'project_domain_id': 'fake_project_domain_id'}
class BaseClientTests(testtools.TestCase):
@ -55,7 +59,8 @@ class BaseClientTests(testtools.TestCase):
}
client.client(username='kingbird', project_name='kingbird',
auth_url=AUTH_HTTP_URL, api_key='password')
auth_url=AUTH_HTTP_URL, api_key='password',
**FAKE_KWARGS)
self.assertTrue(mock.called)
self.assertEqual(mock.call_args[0], expected_args)
self.assertDictEqual(mock.call_args[1], expected_kwargs)
@ -83,7 +88,8 @@ class BaseClientTests(testtools.TestCase):
client.client(kingbird_url=KINGBIRD_HTTPS_URL, username='kingbird',
project_name='kingbird', auth_url=AUTH_HTTP_URL,
api_key='password', cacert=None, insecure=True)
api_key='password', cacert=None, insecure=True,
**FAKE_KWARGS)
self.assertTrue(mock.called)
self.assertEqual(mock.call_args[0], expected_args)
@ -118,8 +124,7 @@ class BaseClientTests(testtools.TestCase):
auth_url=AUTH_HTTP_URL,
api_key='password',
cacert=path,
insecure=False
)
insecure=False, **FAKE_KWARGS)
finally:
os.close(fd)
os.unlink(path)
@ -139,8 +144,7 @@ class BaseClientTests(testtools.TestCase):
api_key='password',
auth_url=AUTH_HTTP_URL,
cacert='/path/to/foobar',
insecure=False
)
insecure=False, **FAKE_KWARGS)
@mock.patch('logging.Logger.warning')
@mock.patch('keystoneauth1.session.Session')
@ -156,8 +160,8 @@ class BaseClientTests(testtools.TestCase):
api_key='password',
auth_url=AUTH_HTTP_URL,
cacert=path,
insecure=True
)
insecure=True,
**FAKE_KWARGS)
finally:
os.close(fd)
os.unlink(path)
@ -189,8 +193,8 @@ class BaseClientTests(testtools.TestCase):
project_name='kingbird',
auth_url=AUTH_HTTP_URL,
api_key='password',
profile=PROFILER_HMAC_KEY
)
profile=PROFILER_HMAC_KEY,
**FAKE_KWARGS)
self.assertTrue(mock.called)
self.assertEqual(mock.call_args[0], expected_args)
@ -204,18 +208,18 @@ class BaseClientTests(testtools.TestCase):
self.assertRaises(RuntimeError, client.client,
kingbird_url=KINGBIRD_HTTP_URL,
username='kingbird', project_name='kingbird',
auth_url=AUTH_HTTP_URL)
auth_url=AUTH_HTTP_URL, **FAKE_KWARGS)
def test_project_name_and_project_id(self):
self.assertRaises(RuntimeError, client.client,
kingbird_url=KINGBIRD_HTTP_URL,
username='kingbird', project_name='kingbird',
project_id=str(uuid.uuid4()),
auth_url=AUTH_HTTP_URL)
auth_url=AUTH_HTTP_URL, **FAKE_KWARGS)
def test_user_name_and_user_id(self):
self.assertRaises(RuntimeError, client.client,
kingbird_url=KINGBIRD_HTTP_URL,
username='kingbird', project_name='kingbird',
user_id=str(uuid.uuid4()),
auth_url=AUTH_HTTP_URL)
auth_url=AUTH_HTTP_URL, **FAKE_KWARGS)

View File

@ -145,3 +145,45 @@ class TestShell(base.BaseShellTests):
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual(None, params[1]['profile'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_project_name(self, mock):
self.shell('--os-project-name default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['project_name'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_tenant_name(self, mock):
self.shell('--os-tenant-name default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['project_name'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_project_domain_name(self, mock):
self.shell('--os-project-domain-name default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['project_domain_name'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_project_domain_id(self, mock):
self.shell('--os-project-domain-id default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['project_domain_id'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_user_domain_name(self, mock):
self.shell('--os-user-domain-name default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['user_domain_name'])
@mock.patch('kingbirdclient.api.client.client')
def test_kingbird_user_domain_id(self, mock):
self.shell('--os-user-domain-id default quota defaults')
self.assertTrue(mock.called)
params = mock.call_args
self.assertEqual('default', params[1]['user_domain_id'])