summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/compute/v2/security_group.py5
-rw-r--r--openstackclient/identity/common.py97
-rw-r--r--openstackclient/shell.py18
-rw-r--r--openstackclient/tests/compute/v2/fakes.py6
-rw-r--r--openstackclient/tests/compute/v2/test_security_group.py197
-rw-r--r--openstackclient/volume/v1/backup.py2
6 files changed, 255 insertions, 70 deletions
diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py
index d4643438..55405810 100644
--- a/openstackclient/compute/v2/security_group.py
+++ b/openstackclient/compute/v2/security_group.py
@@ -81,9 +81,11 @@ class CreateSecurityGroup(show.ShowOne):
compute_client = self.app.client_manager.compute
+ description = parsed_args.description or parsed_args.name
+
data = compute_client.security_groups.create(
parsed_args.name,
- parsed_args.description,
+ description,
)
info = {}
@@ -290,6 +292,7 @@ class CreateSecurityGroupRule(show.ShowOne):
parser.add_argument(
"--dst-port",
metavar="<port-range>",
+ default=(0, 0),
action=parseractions.RangeAction,
help="Destination port, may be a range: 137:139 (default: 0; "
"only required for proto tcp and udp)",
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index a1b46cb4..2cc68c8d 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -20,6 +20,7 @@ from keystoneclient.v3 import domains
from keystoneclient.v3 import groups
from keystoneclient.v3 import projects
from keystoneclient.v3 import users
+
from openstackclient.common import exceptions
from openstackclient.common import utils
@@ -43,74 +44,58 @@ def find_service(identity_client, name_type_or_id):
def find_domain(identity_client, name_or_id):
- """Find a domain.
+ return _find_identity_resource(identity_client.domains, name_or_id,
+ domains.Domain)
- If the user does not have permissions to access the v3 domain API, e.g.,
- if the user is a project admin, assume that the domain given is the id
- rather than the name. This method is used by the project list command,
- so errors accessing the domain will be ignored and if the user has
- access to the project API, everything will work fine.
- Closes bugs #1317478 and #1317485.
- """
- try:
- dom = utils.find_resource(identity_client.domains, name_or_id)
- if dom is not None:
- return dom
- except identity_exc.Forbidden:
- pass
- return domains.Domain(None, {'id': name_or_id, 'name': name_or_id})
+def find_group(identity_client, name_or_id):
+ return _find_identity_resource(identity_client.groups, name_or_id,
+ groups.Group)
-def find_group(identity_client, name_or_id):
- """Find a group.
+def find_project(identity_client, name_or_id):
+ return _find_identity_resource(identity_client.projects, name_or_id,
+ projects.Project)
- If the user does not have permissions to to perform a list groups call,
- e.g., if the user is a project admin, assume that the group given is the
- id rather than the name. This method is used by the role add command to
- allow a role to be assigned to a group by a project admin who does not
- have permission to list groups.
- """
- try:
- group = utils.find_resource(identity_client.groups, name_or_id)
- if group is not None:
- return group
- except identity_exc.Forbidden:
- pass
- return groups.Group(None, {'id': name_or_id, 'name': name_or_id})
+def find_user(identity_client, name_or_id):
+ return _find_identity_resource(identity_client.users, name_or_id,
+ users.User)
-def find_project(identity_client, name_or_id):
- """Find a project.
- If the user does not have permissions to to perform a list projects
- call, e.g., if the user is a project admin, assume that the project
- given is the id rather than the name. This method is used by the role
- add command to allow a role to be assigned to a user by a project admin
- who does not have permission to list projects.
- """
- try:
- project = utils.find_resource(identity_client.projects, name_or_id)
- if project is not None:
- return project
- except identity_exc.Forbidden:
- pass
- return projects.Project(None, {'id': name_or_id, 'name': name_or_id})
+def _find_identity_resource(identity_client_manager, name_or_id,
+ resource_type):
+ """Find a specific identity resource.
+ Using keystoneclient's manager, attempt to find a specific resource by its
+ name or ID. If Forbidden to find the resource (a common case if the user
+ does not have permission), then return the resource by creating a local
+ instance of keystoneclient's Resource.
-def find_user(identity_client, name_or_id):
- """Find a user.
+ The parameter identity_client_manager is a keystoneclient manager,
+ for example: keystoneclient.v3.users or keystoneclient.v3.projects.
+
+ The parameter resource_type is a keystoneclient resource, for example:
+ keystoneclient.v3.users.User or keystoneclient.v3.projects.Project.
+
+ :param identity_client_manager: the manager that contains the resource
+ :type identity_client_manager: `keystoneclient.base.CrudManager`
+ :param name_or_id: the resources's name or ID
+ :type name_or_id: string
+ :param resource_type: class that represents the resource type
+ :type resource_type: `keystoneclient.base.Resource`
+
+ :returns: the resource in question
+ :rtype: `keystoneclient.base.Resource`
- If the user does not have permissions to to perform a list users call,
- e.g., if the user is a project admin, assume that the user given is the
- id rather than the name. This method is used by the role add command to
- allow a role to be assigned to a user by a project admin who does not
- have permission to list users.
"""
+
try:
- user = utils.find_resource(identity_client.users, name_or_id)
- if user is not None:
- return user
+ identity_resource = utils.find_resource(identity_client_manager,
+ name_or_id)
+ if identity_resource is not None:
+ return identity_resource
except identity_exc.Forbidden:
pass
- return users.User(None, {'id': name_or_id, 'name': name_or_id})
+
+ return resource_type(None, {'id': name_or_id, 'name': name_or_id})
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 00f4a3c9..5e291021 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -24,6 +24,7 @@ import warnings
from cliff import app
from cliff import command
+from cliff import complete
from cliff import help
import openstackclient
@@ -76,6 +77,7 @@ class OpenStackShell(app.App):
# Some commands do not need authentication
help.HelpCommand.auth_required = False
+ complete.CompleteCommand.auth_required = False
super(OpenStackShell, self).__init__(
description=__doc__.strip(),
@@ -137,12 +139,11 @@ class OpenStackShell(app.App):
# --debug forces traceback
self.dump_stack_trace = True
requests_log.setLevel(logging.DEBUG)
- cliff_log.setLevel(logging.DEBUG)
else:
self.dump_stack_trace = False
requests_log.setLevel(logging.ERROR)
- cliff_log.setLevel(logging.ERROR)
+ cliff_log.setLevel(logging.ERROR)
stevedore_log.setLevel(logging.ERROR)
iso8601_log.setLevel(logging.ERROR)
@@ -318,19 +319,12 @@ class OpenStackShell(app.App):
cmd.__class__.__name__,
)
if cmd.auth_required:
- try:
- # Trigger the Identity client to initialize
- self.client_manager.auth_ref
- except Exception as e:
- self.log.warning("Possible error authenticating: " + str(e))
- pass
+ # Trigger the Identity client to initialize
+ self.client_manager.auth_ref
return
def clean_up(self, cmd, result, err):
- self.log.debug('clean_up %s', cmd.__class__.__name__)
-
- if err:
- self.log.debug('got an error: %s', err)
+ self.log.debug('clean_up %s: %s', cmd.__class__.__name__, err or '')
# Process collected timing data
if self.options.timing:
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index a22c1ce0..c18dea7e 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -16,6 +16,7 @@
import mock
from openstackclient.tests import fakes
+from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils
@@ -85,6 +86,11 @@ class TestComputev2(utils.TestCommand):
token=fakes.AUTH_TOKEN,
)
+ self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
self.app.client_manager.image = image_fakes.FakeImagev2Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
diff --git a/openstackclient/tests/compute/v2/test_security_group.py b/openstackclient/tests/compute/v2/test_security_group.py
new file mode 100644
index 00000000..fdb659a8
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_security_group.py
@@ -0,0 +1,197 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import copy
+import mock
+
+from openstackclient.compute.v2 import security_group
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
+
+
+security_group_id = '11'
+security_group_name = 'wide-open'
+security_group_description = 'nothing but net'
+
+SECURITY_GROUP = {
+ 'id': security_group_id,
+ 'name': security_group_name,
+ 'description': security_group_description,
+ 'tenant_id': identity_fakes.project_id,
+}
+
+
+class FakeSecurityGroupResource(fakes.FakeResource):
+
+ def get_keys(self):
+ return {'property': 'value'}
+
+
+class TestSecurityGroup(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestSecurityGroup, self).setUp()
+
+ self.secgroups_mock = mock.Mock()
+ self.secgroups_mock.resource_class = fakes.FakeResource(None, {})
+ self.app.client_manager.compute.security_groups = self.secgroups_mock
+ self.secgroups_mock.reset_mock()
+
+ self.projects_mock = mock.Mock()
+ self.projects_mock.resource_class = fakes.FakeResource(None, {})
+ self.app.client_manager.identity.projects = self.projects_mock
+ self.projects_mock.reset_mock()
+
+
+class TestSecurityGroupCreate(TestSecurityGroup):
+
+ def setUp(self):
+ super(TestSecurityGroupCreate, self).setUp()
+
+ self.secgroups_mock.create.return_value = FakeSecurityGroupResource(
+ None,
+ copy.deepcopy(SECURITY_GROUP),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = security_group.CreateSecurityGroup(self.app, None)
+
+ def test_security_group_create_no_options(self):
+ arglist = [
+ security_group_name,
+ ]
+ verifylist = [
+ ('name', security_group_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # SecurityGroupManager.create(name, description)
+ self.secgroups_mock.create.assert_called_with(
+ security_group_name,
+ security_group_name,
+ )
+
+ collist = (
+ 'description',
+ 'id',
+ 'name',
+ 'tenant_id',
+ )
+ self.assertEqual(collist, columns)
+ datalist = (
+ security_group_description,
+ security_group_id,
+ security_group_name,
+ identity_fakes.project_id,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_security_group_create_description(self):
+ arglist = [
+ security_group_name,
+ '--description', security_group_description,
+ ]
+ verifylist = [
+ ('name', security_group_name),
+ ('description', security_group_description),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # SecurityGroupManager.create(name, description)
+ self.secgroups_mock.create.assert_called_with(
+ security_group_name,
+ security_group_description,
+ )
+
+ collist = (
+ 'description',
+ 'id',
+ 'name',
+ 'tenant_id',
+ )
+ self.assertEqual(collist, columns)
+ datalist = (
+ security_group_description,
+ security_group_id,
+ security_group_name,
+ identity_fakes.project_id,
+ )
+ self.assertEqual(datalist, data)
+
+
+class TestSecurityGroupList(TestSecurityGroup):
+
+ def setUp(self):
+ super(TestSecurityGroupList, self).setUp()
+
+ self.secgroups_mock.list.return_value = [
+ FakeSecurityGroupResource(
+ None,
+ copy.deepcopy(SECURITY_GROUP),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = security_group.ListSecurityGroup(self.app, None)
+
+ def test_security_group_list_no_options(self):
+ self.projects_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ ),
+ ]
+
+ arglist = []
+ verifylist = [
+ ('all_projects', False),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'search_opts': {
+ 'all_tenants': False,
+ },
+ }
+
+ self.secgroups_mock.list.assert_called_with(
+ **kwargs
+ )
+
+ collist = (
+ 'ID',
+ 'Name',
+ 'Description',
+ )
+ self.assertEqual(collist, columns)
+ datalist = ((
+ security_group_id,
+ security_group_name,
+ security_group_description,
+ ), )
+ self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py
index 71c8ed38..03c63a05 100644
--- a/openstackclient/volume/v1/backup.py
+++ b/openstackclient/volume/v1/backup.py
@@ -64,7 +64,7 @@ class CreateBackup(show.ShowOne):
parsed_args.volume).id
backup = volume_client.backups.create(
volume_id,
- parsed_args.volume,
+ parsed_args.container,
parsed_args.name,
parsed_args.description
)