summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/auth.py39
-rw-r--r--openstackclient/api/object_store_v1.py39
-rw-r--r--openstackclient/common/clientmanager.py34
-rw-r--r--openstackclient/common/exceptions.py4
-rw-r--r--openstackclient/common/logs.py4
-rw-r--r--openstackclient/common/utils.py14
-rw-r--r--openstackclient/identity/v2_0/token.py6
-rw-r--r--openstackclient/identity/v3/token.py3
-rw-r--r--openstackclient/network/v2/subnet_pool.py4
-rw-r--r--openstackclient/shell.py3
-rw-r--r--openstackclient/tests/common/test_clientmanager.py25
-rw-r--r--openstackclient/tests/identity/v2_0/fakes.py6
-rw-r--r--openstackclient/tests/identity/v2_0/test_token.py22
-rw-r--r--openstackclient/tests/identity/v3/fakes.py6
-rw-r--r--openstackclient/tests/identity/v3/test_token.py21
-rw-r--r--openstackclient/tests/network/v2/fakes.py19
16 files changed, 183 insertions, 66 deletions
diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py
index 44287318..3d6f7bcf 100644
--- a/openstackclient/api/auth.py
+++ b/openstackclient/api/auth.py
@@ -80,13 +80,13 @@ def select_auth_plugin(options):
# Do the token/url check first as this must override the default
# 'password' set by os-client-config
# Also, url and token are not copied into o-c-c's auth dict (yet?)
- if options.auth.get('url', None) and options.auth.get('token', None):
+ if options.auth.get('url') and options.auth.get('token'):
# service token authentication
auth_plugin_name = 'token_endpoint'
elif options.auth_type in [plugin.name for plugin in PLUGIN_LIST]:
# A direct plugin name was given, use it
auth_plugin_name = options.auth_type
- elif options.auth.get('username', None):
+ elif options.auth.get('username'):
if options.identity_api_version == '3':
auth_plugin_name = 'v3password'
elif options.identity_api_version.startswith('2'):
@@ -94,7 +94,7 @@ def select_auth_plugin(options):
else:
# let keystoneclient figure it out itself
auth_plugin_name = 'osc_password'
- elif options.auth.get('token', None):
+ elif options.auth.get('token'):
if options.identity_api_version == '3':
auth_plugin_name = 'v3token'
elif options.identity_api_version.startswith('2'):
@@ -135,37 +135,42 @@ def build_auth_params(auth_plugin_name, cmd_options):
return (auth_plugin_class, auth_params)
-def check_valid_auth_options(options, auth_plugin_name):
- """Perform basic option checking, provide helpful error messages"""
+def check_valid_auth_options(options, auth_plugin_name, required_scope=True):
+ """Perform basic option checking, provide helpful error messages.
+
+ :param required_scope: indicate whether a scoped token is required
+
+ """
msg = ''
if auth_plugin_name.endswith('password'):
- if not options.auth.get('username', None):
+ if not options.auth.get('username'):
msg += _('Set a username with --os-username, OS_USERNAME,'
' or auth.username\n')
- if not options.auth.get('auth_url', None):
+ if not options.auth.get('auth_url'):
msg += _('Set an authentication URL, with --os-auth-url,'
' OS_AUTH_URL or auth.auth_url\n')
- if (not options.auth.get('project_id', None) and not
- options.auth.get('domain_id', None) and not
- options.auth.get('domain_name', None) and not
- options.auth.get('project_name', None) and not
- options.auth.get('tenant_id', None) and not
- options.auth.get('tenant_name', None)):
+ if (required_scope and not
+ options.auth.get('project_id') and not
+ options.auth.get('domain_id') and not
+ options.auth.get('domain_name') and not
+ options.auth.get('project_name') and not
+ options.auth.get('tenant_id') and not
+ options.auth.get('tenant_name')):
msg += _('Set a scope, such as a project or domain, set a '
'project scope with --os-project-name, OS_PROJECT_NAME '
'or auth.project_name, set a domain scope with '
'--os-domain-name, OS_DOMAIN_NAME or auth.domain_name')
elif auth_plugin_name.endswith('token'):
- if not options.auth.get('token', None):
+ if not options.auth.get('token'):
msg += _('Set a token with --os-token, OS_TOKEN or auth.token\n')
- if not options.auth.get('auth_url', None):
+ if not options.auth.get('auth_url'):
msg += _('Set a service AUTH_URL, with --os-auth-url, '
'OS_AUTH_URL or auth.auth_url\n')
elif auth_plugin_name == 'token_endpoint':
- if not options.auth.get('token', None):
+ if not options.auth.get('token'):
msg += _('Set a token with --os-token, OS_TOKEN or auth.token\n')
- if not options.auth.get('url', None):
+ if not options.auth.get('url'):
msg += _('Set a service URL, with --os-url, OS_URL or auth.url\n')
if msg:
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index d9f130bc..307c8fe2 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -50,7 +50,7 @@ class APIv1(api.BaseAPI):
data = {
'account': self._find_account_id(),
'container': container,
- 'x-trans-id': response.headers.get('x-trans-id', None),
+ 'x-trans-id': response.headers.get('x-trans-id'),
}
return data
@@ -176,21 +176,19 @@ class APIv1(api.BaseAPI):
'account': self._find_account_id(),
'container': container,
'object_count': response.headers.get(
- 'x-container-object-count',
- None,
+ 'x-container-object-count'
),
- 'bytes_used': response.headers.get('x-container-bytes-used', None)
+ 'bytes_used': response.headers.get('x-container-bytes-used')
}
if 'x-container-read' in response.headers:
- data['read_acl'] = response.headers.get('x-container-read', None)
+ data['read_acl'] = response.headers.get('x-container-read')
if 'x-container-write' in response.headers:
- data['write_acl'] = response.headers.get('x-container-write', None)
+ data['write_acl'] = response.headers.get('x-container-write')
if 'x-container-sync-to' in response.headers:
- data['sync_to'] = response.headers.get('x-container-sync-to', None)
+ data['sync_to'] = response.headers.get('x-container-sync-to')
if 'x-container-sync-key' in response.headers:
- data['sync_key'] = response.headers.get('x-container-sync-key',
- None)
+ data['sync_key'] = response.headers.get('x-container-sync-key')
properties = self._get_properties(response.headers,
'x-container-meta-')
@@ -248,8 +246,8 @@ class APIv1(api.BaseAPI):
'account': self._find_account_id(),
'container': container,
'object': object,
- 'x-trans-id': response.headers.get('X-Trans-Id', None),
- 'etag': response.headers.get('Etag', None),
+ 'x-trans-id': response.headers.get('X-Trans-Id'),
+ 'etag': response.headers.get('Etag'),
}
return data
@@ -453,21 +451,19 @@ class APIv1(api.BaseAPI):
'account': self._find_account_id(),
'container': container,
'object': object,
- 'content-type': response.headers.get('content-type', None),
+ 'content-type': response.headers.get('content-type'),
}
if 'content-length' in response.headers:
data['content-length'] = response.headers.get(
- 'content-length',
- None,
+ 'content-length'
)
if 'last-modified' in response.headers:
- data['last-modified'] = response.headers.get('last-modified', None)
+ data['last-modified'] = response.headers.get('last-modified')
if 'etag' in response.headers:
- data['etag'] = response.headers.get('etag', None)
+ data['etag'] = response.headers.get('etag')
if 'x-object-manifest' in response.headers:
data['x-object-manifest'] = response.headers.get(
- 'x-object-manifest',
- None,
+ 'x-object-manifest'
)
properties = self._get_properties(response.headers, 'x-object-meta-')
@@ -506,10 +502,9 @@ class APIv1(api.BaseAPI):
data['properties'] = properties
# Map containers, bytes and objects a bit nicer
- data['Containers'] = response.headers.get('x-account-container-count',
- None)
- data['Objects'] = response.headers.get('x-account-object-count', None)
- data['Bytes'] = response.headers.get('x-account-bytes-used', None)
+ data['Containers'] = response.headers.get('x-account-container-count')
+ data['Objects'] = response.headers.get('x-account-object-count')
+ data['Bytes'] = response.headers.get('x-account-bytes-used')
# Add in Account info too
data['Account'] = self._find_account_id()
return data
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 5696b9e1..fd88bdec 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -90,7 +90,7 @@ class ClientManager(object):
self._cli_options = cli_options
self._api_version = api_version
self._pw_callback = pw_func
- self._url = self._cli_options.auth.get('url', None)
+ self._url = self._cli_options.auth.get('url')
self._region_name = self._cli_options.region_name
self._interface = self._cli_options.interface
@@ -113,24 +113,40 @@ class ClientManager(object):
root_logger = logging.getLogger('')
LOG.setLevel(root_logger.getEffectiveLevel())
- def setup_auth(self):
+ # NOTE(gyee): use this flag to indicate whether auth setup has already
+ # been completed. If so, do not perform auth setup again. The reason
+ # we need this flag is that we want to be able to perform auth setup
+ # outside of auth_ref as auth_ref itself is a property. We can not
+ # retrofit auth_ref to optionally skip scope check. Some operations
+ # do not require a scoped token. In those cases, we call setup_auth
+ # prior to dereferrencing auth_ref.
+ self._auth_setup_completed = False
+
+ def setup_auth(self, required_scope=True):
"""Set up authentication
+ :param required_scope: indicate whether a scoped token is required
+
This is deferred until authentication is actually attempted because
it gets in the way of things that do not require auth.
"""
+ if self._auth_setup_completed:
+ return
+
# If no auth type is named by the user, select one based on
# the supplied options
self.auth_plugin_name = auth.select_auth_plugin(self._cli_options)
# Basic option checking to avoid unhelpful error messages
- auth.check_valid_auth_options(self._cli_options, self.auth_plugin_name)
+ auth.check_valid_auth_options(self._cli_options,
+ self.auth_plugin_name,
+ required_scope=required_scope)
# Horrible hack alert...must handle prompt for null password if
# password auth is requested.
if (self.auth_plugin_name.endswith('password') and
- not self._cli_options.auth.get('password', None)):
+ not self._cli_options.auth.get('password')):
self._cli_options.auth['password'] = self._pw_callback()
(auth_plugin, self._auth_params) = auth.build_auth_params(
@@ -146,9 +162,9 @@ class ClientManager(object):
# PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
if (self._api_version.get('identity') == '3' and
self.auth_plugin_name.endswith('password') and
- not self._auth_params.get('project_domain_id', None) and
+ not self._auth_params.get('project_domain_id') and
not self.auth_plugin_name.startswith('v2') and
- not self._auth_params.get('project_domain_name', None)):
+ not self._auth_params.get('project_domain_name')):
self._auth_params['project_domain_id'] = default_domain
# NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
@@ -157,8 +173,8 @@ class ClientManager(object):
if (self._api_version.get('identity') == '3' and
self.auth_plugin_name.endswith('password') and
not self.auth_plugin_name.startswith('v2') and
- not self._auth_params.get('user_domain_id', None) and
- not self._auth_params.get('user_domain_name', None)):
+ not self._auth_params.get('user_domain_id') and
+ not self._auth_params.get('user_domain_name')):
self._auth_params['user_domain_id'] = default_domain
# For compatibility until all clients can be updated
@@ -180,6 +196,8 @@ class ClientManager(object):
user_agent=USER_AGENT,
)
+ self._auth_setup_completed = True
+
return
@property
diff --git a/openstackclient/common/exceptions.py b/openstackclient/common/exceptions.py
index ab043db0..8ec49931 100644
--- a/openstackclient/common/exceptions.py
+++ b/openstackclient/common/exceptions.py
@@ -122,8 +122,8 @@ def from_response(response, body):
if body:
if hasattr(body, 'keys'):
error = body[body.keys()[0]]
- message = error.get('message', None)
- details = error.get('details', None)
+ message = error.get('message')
+ details = error.get('details')
else:
# If we didn't get back a properly formed error message we
# probably couldn't communicate with Keystone at all.
diff --git a/openstackclient/common/logs.py b/openstackclient/common/logs.py
index 7ad6e832..221c5997 100644
--- a/openstackclient/common/logs.py
+++ b/openstackclient/common/logs.py
@@ -169,7 +169,7 @@ class LogConfigurator(object):
self.dump_trace = cloud_config.config.get('debug', self.dump_trace)
self.console_logger.setLevel(log_level)
- log_file = cloud_config.config.get('log_file', None)
+ log_file = cloud_config.config.get('log_file')
if log_file:
if not self.file_logger:
self.file_logger = logging.FileHandler(filename=log_file)
@@ -179,7 +179,7 @@ class LogConfigurator(object):
self.file_logger.setLevel(log_level)
self.root_logger.addHandler(self.file_logger)
- logconfig = cloud_config.config.get('logging', None)
+ logconfig = cloud_config.config.get('logging')
if logconfig:
highest_level = logging.NOTSET
for k in logconfig.keys():
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index 4142f830..840da402 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -163,7 +163,7 @@ def get_field(item, field):
raise exceptions.CommandError(msg)
-def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
+def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
"""Return a tuple containing the item properties.
:param item: a single item resource (e.g. Server, Project, etc)
@@ -172,6 +172,11 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
:param formatters: dictionary mapping field names to callables
to format the values
"""
+ if mixed_case_fields is None:
+ mixed_case_fields = []
+ if formatters is None:
+ formatters = {}
+
row = []
for field in fields:
@@ -187,7 +192,7 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
return tuple(row)
-def get_dict_properties(item, fields, mixed_case_fields=[], formatters={}):
+def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None):
"""Return a tuple containing the item properties.
:param item: a single dict resource
@@ -196,6 +201,11 @@ def get_dict_properties(item, fields, mixed_case_fields=[], formatters={}):
:param formatters: dictionary mapping field names to callables
to format the values
"""
+ if mixed_case_fields is None:
+ mixed_case_fields = []
+ if formatters is None:
+ formatters = {}
+
row = []
for field in fields:
diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py
index db38fae8..6a66a1c6 100644
--- a/openstackclient/identity/v2_0/token.py
+++ b/openstackclient/identity/v2_0/token.py
@@ -24,6 +24,9 @@ from openstackclient.i18n import _ # noqa
class IssueToken(command.ShowOne):
"""Issue new token"""
+ # scoped token is optional
+ required_scope = False
+
def get_parser(self, prog_name):
parser = super(IssueToken, self).get_parser(prog_name)
return parser
@@ -31,7 +34,8 @@ class IssueToken(command.ShowOne):
def take_action(self, parsed_args):
token = self.app.client_manager.auth_ref.service_catalog.get_token()
- token['project_id'] = token.pop('tenant_id')
+ if 'tenant_id' in token:
+ token['project_id'] = token.pop('tenant_id')
return zip(*sorted(six.iteritems(token)))
diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py
index 9ebd1799..5f131939 100644
--- a/openstackclient/identity/v3/token.py
+++ b/openstackclient/identity/v3/token.py
@@ -164,6 +164,9 @@ class CreateRequestToken(command.ShowOne):
class IssueToken(command.ShowOne):
"""Issue new token"""
+ # scoped token is optional
+ required_scope = False
+
def get_parser(self, prog_name):
parser = super(IssueToken, self).get_parser(prog_name)
return parser
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 1db1652f..5bb45c12 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -99,14 +99,14 @@ class ListSubnetPool(command.Lister):
class ShowSubnetPool(command.ShowOne):
- """Show subnet pool details"""
+ """Display subnet pool details"""
def get_parser(self, prog_name):
parser = super(ShowSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'subnet_pool',
metavar="<subnet-pool>",
- help=("Subnet pool to show (name or ID)")
+ help=("Subnet pool to display (name or ID)")
)
return parser
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 137446ef..dfec40af 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -353,6 +353,9 @@ class OpenStackShell(app.App):
cmd.__class__.__name__,
)
if cmd.auth_required:
+ if hasattr(cmd, 'required_scope'):
+ # let the command decide whether we need a scoped token
+ self.client_manager.setup_auth(cmd.required_scope)
# Trigger the Identity client to initialize
self.client_manager.auth_ref
return
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 523f79a3..ef46f61c 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -325,3 +325,28 @@ class TestClientManager(utils.TestCase):
exc.CommandError,
client_manager.setup_auth,
)
+
+ @mock.patch('openstackclient.api.auth.check_valid_auth_options')
+ def test_client_manager_auth_setup_once(self, check_auth_options_func):
+ client_manager = clientmanager.ClientManager(
+ cli_options=FakeOptions(
+ auth=dict(
+ auth_url=fakes.AUTH_URL,
+ username=fakes.USERNAME,
+ password=fakes.PASSWORD,
+ project_name=fakes.PROJECT_NAME,
+ ),
+ ),
+ api_version=API_VERSION,
+ verify=False,
+ )
+ self.assertFalse(client_manager._auth_setup_completed)
+ client_manager.setup_auth()
+ self.assertTrue(check_auth_options_func.called)
+ self.assertTrue(client_manager._auth_setup_completed)
+
+ # now make sure we don't do auth setup the second time around
+ # by checking whether check_valid_auth_options() gets called again
+ check_auth_options_func.reset_mock()
+ client_manager.auth_ref
+ check_auth_options_func.assert_not_called()
diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py
index 6688606a..565606c1 100644
--- a/openstackclient/tests/identity/v2_0/fakes.py
+++ b/openstackclient/tests/identity/v2_0/fakes.py
@@ -80,6 +80,12 @@ TOKEN = {
'user_id': user_id,
}
+UNSCOPED_TOKEN = {
+ 'expires': token_expires,
+ 'id': token_id,
+ 'user_id': user_id,
+}
+
endpoint_name = service_name
endpoint_adminurl = 'https://admin.example.com/v2/UUID'
endpoint_region = 'RegionOne'
diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py
index 7687a063..c90477f9 100644
--- a/openstackclient/tests/identity/v2_0/test_token.py
+++ b/openstackclient/tests/identity/v2_0/test_token.py
@@ -60,6 +60,28 @@ class TestTokenIssue(TestToken):
)
self.assertEqual(datalist, data)
+ def test_token_issue_with_unscoped_token(self):
+ # make sure we return an unscoped token
+ self.sc_mock.get_token.return_value = identity_fakes.UNSCOPED_TOKEN
+
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.sc_mock.get_token.assert_called_with()
+
+ collist = ('expires', 'id', 'user_id')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.token_expires,
+ identity_fakes.token_id,
+ identity_fakes.user_id,
+ )
+ self.assertEqual(datalist, data)
+
class TestTokenRevoke(TestToken):
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index a06802c5..420604f1 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -244,6 +244,12 @@ TRUST = {
token_expires = '2014-01-01T00:00:00Z'
token_id = 'tttttttt-tttt-tttt-tttt-tttttttttttt'
+UNSCOPED_TOKEN = {
+ 'expires': token_expires,
+ 'id': token_id,
+ 'user_id': user_id,
+}
+
TOKEN_WITH_PROJECT_ID = {
'expires': token_expires,
'id': token_id,
diff --git a/openstackclient/tests/identity/v3/test_token.py b/openstackclient/tests/identity/v3/test_token.py
index b051aacb..80c397bc 100644
--- a/openstackclient/tests/identity/v3/test_token.py
+++ b/openstackclient/tests/identity/v3/test_token.py
@@ -85,6 +85,27 @@ class TestTokenIssue(TestToken):
)
self.assertEqual(datalist, data)
+ def test_token_issue_with_unscoped(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.sc_mock.get_token.return_value = \
+ identity_fakes.UNSCOPED_TOKEN
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.sc_mock.get_token.assert_called_with()
+
+ collist = ('expires', 'id', 'user_id')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.token_expires,
+ identity_fakes.token_id,
+ identity_fakes.user_id,
+ )
+ self.assertEqual(datalist, data)
+
class TestTokenRevoke(TestToken):
diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py
index 60895e74..680e9cbf 100644
--- a/openstackclient/tests/network/v2/fakes.py
+++ b/openstackclient/tests/network/v2/fakes.py
@@ -138,12 +138,11 @@ class FakeNetwork(object):
router_external, status, subnets, tenant_id
"""
# Set default attributes.
- project_id = 'project-id-' + uuid.uuid4().hex
network_attrs = {
'id': 'network-id-' + uuid.uuid4().hex,
'name': 'network-name-' + uuid.uuid4().hex,
'status': 'ACTIVE',
- 'tenant_id': project_id,
+ 'tenant_id': 'project-id-' + uuid.uuid4().hex,
'admin_state_up': True,
'shared': False,
'subnets': ['a', 'b'],
@@ -169,7 +168,9 @@ class FakeNetwork(object):
network = fakes.FakeResource(info=copy.deepcopy(network_attrs),
methods=copy.deepcopy(network_methods),
loaded=True)
- network.project_id = project_id
+
+ # Set attributes with special mapping in OpenStack SDK.
+ network.project_id = network_attrs['tenant_id']
return network
@@ -273,7 +274,7 @@ class FakePort(object):
methods=copy.deepcopy(port_methods),
loaded=True)
- # Set attributes with special mappings.
+ # Set attributes with special mappings in OpenStack SDK.
port.project_id = port_attrs['tenant_id']
port.binding_host_id = port_attrs['binding:host_id']
port.binding_profile = port_attrs['binding:profile']
@@ -695,24 +696,19 @@ class FakeSubnetPool(object):
A FakeResource object faking the subnet pool
"""
# Set default attributes.
- project_id = 'project-id-' + uuid.uuid4().hex
subnet_pool_attrs = {
'id': 'subnet-pool-id-' + uuid.uuid4().hex,
'name': 'subnet-pool-name-' + uuid.uuid4().hex,
'prefixes': ['10.0.0.0/24', '10.1.0.0/24'],
'default_prefixlen': 8,
'address_scope_id': 'address-scope-id-' + uuid.uuid4().hex,
- 'tenant_id': project_id,
+ 'tenant_id': 'project-id-' + uuid.uuid4().hex,
'is_default': False,
'shared': False,
'max_prefixlen': 32,
'min_prefixlen': 8,
'default_quota': None,
'ip_version': 4,
-
- # OpenStack SDK automatically translates project_id to tenant_id.
- # So we need an additional attr to simulate this behavior.
- 'project_id': project_id,
}
# Overwrite default attributes.
@@ -735,6 +731,9 @@ class FakeSubnetPool(object):
loaded=True
)
+ # Set attributes with special mapping in OpenStack SDK.
+ subnet_pool.project_id = subnet_pool_attrs['tenant_id']
+
return subnet_pool
@staticmethod