summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/parseractions.py5
-rw-r--r--openstackclient/common/quota.py29
-rw-r--r--openstackclient/compute/v2/flavor.py6
-rw-r--r--openstackclient/compute/v2/server_group.py182
-rw-r--r--openstackclient/locale/de/LC_MESSAGES/openstackclient.po28
-rw-r--r--openstackclient/locale/openstackclient.pot1045
-rw-r--r--openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po21
-rw-r--r--openstackclient/network/client.py8
-rw-r--r--openstackclient/network/v2/network.py64
-rw-r--r--openstackclient/network/v2/port.py3
-rw-r--r--openstackclient/network/v2/security_group_rule.py46
-rw-r--r--openstackclient/network/v2/subnet.py18
-rw-r--r--openstackclient/network/v2/subnet_pool.py50
-rw-r--r--openstackclient/shell.py39
-rw-r--r--openstackclient/tests/common/test_quota.py11
-rw-r--r--openstackclient/tests/compute/v2/fakes.py93
-rw-r--r--openstackclient/tests/compute/v2/test_flavor.py12
-rw-r--r--openstackclient/tests/compute/v2/test_server_group.py283
-rw-r--r--openstackclient/tests/fakes.py10
-rw-r--r--openstackclient/tests/image/v2/fakes.py6
-rw-r--r--openstackclient/tests/network/v2/fakes.py48
-rw-r--r--openstackclient/tests/network/v2/test_network.py9
-rw-r--r--openstackclient/tests/network/v2/test_security_group_rule.py99
-rw-r--r--openstackclient/tests/network/v2/test_subnet_pool.py126
-rw-r--r--openstackclient/tests/test_shell.py2
-rw-r--r--openstackclient/tests/volume/v1/fakes.py91
-rw-r--r--openstackclient/tests/volume/v1/test_service.py141
-rw-r--r--openstackclient/tests/volume/v2/fakes.py111
-rw-r--r--openstackclient/tests/volume/v2/test_service.py141
-rw-r--r--openstackclient/volume/v1/service.py70
-rw-r--r--openstackclient/volume/v2/service.py70
31 files changed, 2424 insertions, 443 deletions
diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py
index c30058c8..77798f90 100644
--- a/openstackclient/common/parseractions.py
+++ b/openstackclient/common/parseractions.py
@@ -155,9 +155,8 @@ class NonNegativeAction(argparse.Action):
"""
def __call__(self, parser, namespace, values, option_string=None):
- try:
- assert(int(values) >= 0)
+ if int(values) >= 0:
setattr(namespace, self.dest, values)
- except Exception:
+ else:
msg = "%s expected a non-negative integer" % (str(option_string))
raise argparse.ArgumentTypeError(msg)
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index b3d4c3b6..b497a44d 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -145,7 +145,8 @@ class ShowQuota(command.ShowOne):
parser.add_argument(
'project',
metavar='<project/class>',
- help='Show this project or class (name/ID)',
+ nargs='?',
+ help='Show quotas for this project or class (name or ID)',
)
type_group = parser.add_mutually_exclusive_group()
type_group.add_argument(
@@ -164,12 +165,22 @@ class ShowQuota(command.ShowOne):
)
return parser
+ def _get_project(self, parsed_args):
+ if parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ ).id
+ elif self.app.client_manager.auth_ref:
+ # Get the project from the current auth
+ project = self.app.client_manager.auth_ref.project_id
+ else:
+ project = None
+ return project
+
def get_compute_volume_quota(self, client, parsed_args):
- identity_client = self.app.client_manager.identity
- project = utils.find_resource(
- identity_client.projects,
- parsed_args.project,
- ).id
+ project = self._get_project(parsed_args)
try:
if parsed_args.quota_class:
@@ -189,11 +200,7 @@ class ShowQuota(command.ShowOne):
if parsed_args.quota_class or parsed_args.default:
return {}
if self.app.client_manager.is_network_endpoint_enabled():
- identity_client = self.app.client_manager.identity
- project = utils.find_resource(
- identity_client.projects,
- parsed_args.project,
- ).id
+ project = self._get_project(parsed_args)
return self.app.client_manager.network.get_quota(project)
else:
return {}
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 29e0e9d4..04674614 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -239,7 +239,8 @@ class SetFlavor(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = compute_client.flavors.find(name=parsed_args.flavor)
+ flavor = utils.find_resource(compute_client.flavors,
+ parsed_args.flavor)
flavor.set_keys(parsed_args.property)
@@ -289,5 +290,6 @@ class UnsetFlavor(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = compute_client.flavors.find(name=parsed_args.flavor)
+ flavor = utils.find_resource(compute_client.flavors,
+ parsed_args.flavor)
flavor.unset_keys(parsed_args.property)
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
new file mode 100644
index 00000000..eb5745f5
--- /dev/null
+++ b/openstackclient/compute/v2/server_group.py
@@ -0,0 +1,182 @@
+# Copyright 2016 Huawei, Inc. All rights reserved.
+#
+# 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.
+#
+
+"""Compute v2 Server Group action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import exceptions
+from openstackclient.common import utils
+
+
+_formatters = {
+ 'policies': utils.format_list,
+ 'members': utils.format_list,
+}
+
+
+def _get_columns(info):
+ columns = list(info.keys())
+ if 'metadata' in columns:
+ # NOTE(RuiChen): The metadata of server group is always empty since API
+ # compatible, so hide it in order to avoid confusion.
+ columns.remove('metadata')
+ return tuple(sorted(columns))
+
+
+class CreateServerGroup(command.ShowOne):
+ """Create a new server group."""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help='New server group name',
+ )
+ parser.add_argument(
+ '--policy',
+ metavar='<policy>',
+ action='append',
+ required=True,
+ help='Add a policy to <name> '
+ '(repeat option to add multiple policies)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ info = {}
+ server_group = compute_client.server_groups.create(
+ name=parsed_args.name,
+ policies=parsed_args.policy)
+ info.update(server_group._info)
+
+ columns = _get_columns(info)
+ data = utils.get_dict_properties(info, columns,
+ formatters=_formatters)
+ return columns, data
+
+
+class DeleteServerGroup(command.Command):
+ """Delete an existing server group."""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server_group',
+ metavar='<server-group>',
+ nargs='+',
+ help='server group(s) to delete (name or ID)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ result = 0
+ for group in parsed_args.server_group:
+ try:
+ group_obj = utils.find_resource(compute_client.server_groups,
+ group)
+ compute_client.server_groups.delete(group_obj.id)
+ # Catch all exceptions in order to avoid to block the next deleting
+ except Exception as e:
+ result += 1
+ self.app.log.error(e)
+
+ if result > 0:
+ total = len(parsed_args.server_group)
+ msg = "%s of %s server groups failed to delete." % (result, total)
+ raise exceptions.CommandError(msg)
+
+
+class ListServerGroup(command.Lister):
+ """List all server groups."""
+
+ def get_parser(self, prog_name):
+ parser = super(ListServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help='Display information from all projects (admin only)',
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ data = compute_client.server_groups.list(parsed_args.all_projects)
+
+ if parsed_args.long:
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+ columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+ else:
+ column_headers = columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ )
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={
+ 'Policies': utils.format_list,
+ 'Members': utils.format_list,
+ }
+ ) for s in data))
+
+
+class ShowServerGroup(command.ShowOne):
+ """Display server group details."""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server_group',
+ metavar='<server-group>',
+ help='server group to display (name or ID)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ group = utils.find_resource(compute_client.server_groups,
+ parsed_args.server_group)
+ info = {}
+ info.update(group._info)
+ columns = _get_columns(info)
+ data = utils.get_dict_properties(info, columns,
+ formatters=_formatters)
+ return columns, data
diff --git a/openstackclient/locale/de/LC_MESSAGES/openstackclient.po b/openstackclient/locale/de/LC_MESSAGES/openstackclient.po
index 1c94b9d4..f7445b1c 100644
--- a/openstackclient/locale/de/LC_MESSAGES/openstackclient.po
+++ b/openstackclient/locale/de/LC_MESSAGES/openstackclient.po
@@ -5,13 +5,12 @@
#
# Translators:
# Ettore Atalan <atalanttore@googlemail.com>, 2014-2015
-# Andreas Jaeger <jaegerandi@gmail.com>, 2015. #zanata
-# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
+# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
msgid ""
msgstr ""
-"Project-Id-Version: python-openstackclient 2.0.1.dev168\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2016-01-19 02:20+0000\n"
+"Project-Id-Version: python-openstackclient 2.2.1.dev235\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
+"POT-Creation-Date: 2016-04-19 05:02+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -167,10 +166,6 @@ msgid "Endpoint ID to display"
msgstr "Anzuzeigende Endpunktkennung"
#, python-format
-msgid "Error creating server snapshot: %s"
-msgstr "Fehler beim Erstellen der Server-Schattenkopie: %s"
-
-#, python-format
msgid "Error creating server: %s"
msgstr "Fehler beim Erstellen des Servers: %s"
@@ -181,11 +176,6 @@ msgstr "Fehler beim Löschen des Servers: %s"
msgid "Error retrieving diagnostics data"
msgstr "Fehler beim Abrufen der Diagnosedaten"
-msgid "File to inject into image before boot (repeat for multiple files)"
-msgstr ""
-"Vor dem Start auf diesem Abbild einzufügende Datei (für mehrere Dateien "
-"wiederholen)"
-
msgid "Filter by parent region ID"
msgstr "Nach übergeordneter Regionskennung filtern"
@@ -338,11 +328,6 @@ msgstr "Zu löschende(s) Projekt(e) (Name oder Kennung)"
msgid "Prompt interactively for password"
msgstr "Interaktiv nach dem Passwort abfragen"
-msgid "Property key to remove from server (repeat to unset multiple values)"
-msgstr ""
-"Vom Server zu entfernender Eigenschaftsschlüssel (zum Aufheben von mehreren "
-"Werten wiederholen)"
-
msgid ""
"Property to add/change for this server (repeat option to set multiple "
"properties)"
@@ -446,11 +431,6 @@ msgstr ""
"Legen Sie eine Projekteigenschaft fest (wiederholen Sie die Option, um "
"mehrere Eigenschaften festzulegen)"
-msgid "Set a property on this server (repeat for multiple values)"
-msgstr ""
-"Legen Sie eine Eigenschaft auf diesem Server fest (für mehrere Werte "
-"wiederholen)"
-
msgid ""
"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 "
diff --git a/openstackclient/locale/openstackclient.pot b/openstackclient/locale/openstackclient.pot
index afc89266..e197fa7b 100644
--- a/openstackclient/locale/openstackclient.pot
+++ b/openstackclient/locale/openstackclient.pot
@@ -7,9 +7,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: python-openstackclient 2.0.1.dev168\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2016-01-19 06:03+0000\n"
+"Project-Id-Version: python-openstackclient 2.2.1.dev235\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
+"POT-Creation-Date: 2016-04-19 06:14+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,123 +18,142 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.2.0\n"
-#: openstackclient/api/auth.py:144
+#: openstackclient/api/auth.py:148
msgid "Set a username with --os-username, OS_USERNAME, or auth.username\n"
msgstr ""
-#: openstackclient/api/auth.py:147
+#: openstackclient/api/auth.py:151
msgid ""
"Set an authentication URL, with --os-auth-url, OS_AUTH_URL or "
"auth.auth_url\n"
msgstr ""
-#: openstackclient/api/auth.py:155
+#: openstackclient/api/auth.py:160
msgid ""
"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"
msgstr ""
-#: openstackclient/api/auth.py:161 openstackclient/api/auth.py:167
+#: openstackclient/api/auth.py:166 openstackclient/api/auth.py:172
msgid "Set a token with --os-token, OS_TOKEN or auth.token\n"
msgstr ""
-#: openstackclient/api/auth.py:163
+#: openstackclient/api/auth.py:168
msgid "Set a service AUTH_URL, with --os-auth-url, OS_AUTH_URL or auth.auth_url\n"
msgstr ""
-#: openstackclient/api/auth.py:169
+#: openstackclient/api/auth.py:174
msgid "Set a service URL, with --os-url, OS_URL or auth.url\n"
msgstr ""
-#: openstackclient/compute/v2/availability_zone.py:72
-#: openstackclient/compute/v2/server.py:737
-#: openstackclient/identity/v2_0/endpoint.py:114
-#: openstackclient/identity/v2_0/project.py:145
-#: openstackclient/identity/v2_0/service.py:128
-#: openstackclient/identity/v2_0/user.py:174
+#: openstackclient/common/availability_zone.py:110
+#: openstackclient/compute/v2/server.py:757
+#: openstackclient/identity/v2_0/endpoint.py:101
+#: openstackclient/identity/v2_0/project.py:133
+#: openstackclient/identity/v2_0/service.py:115
+#: openstackclient/identity/v2_0/user.py:162
+#: openstackclient/network/v2/router.py:230
+#: openstackclient/network/v2/subnet.py:299
+#: openstackclient/network/v2/subnet_pool.py:165
msgid "List additional fields in output"
msgstr ""
-#: openstackclient/compute/v2/server.py:183
-#: openstackclient/compute/v2/server.py:219
-#: openstackclient/compute/v2/server.py:569
-#: openstackclient/compute/v2/server.py:923
-#: openstackclient/compute/v2/server.py:1031
-#: openstackclient/compute/v2/server.py:1086
-#: openstackclient/compute/v2/server.py:1179
-#: openstackclient/compute/v2/server.py:1219
-#: openstackclient/compute/v2/server.py:1245
-#: openstackclient/compute/v2/server.py:1336
-#: openstackclient/compute/v2/server.py:1420
-#: openstackclient/compute/v2/server.py:1457
-#: openstackclient/compute/v2/server.py:1732
-#: openstackclient/compute/v2/server.py:1756
+#: openstackclient/common/parseractions.py:99
+#, python-format
+msgid ""
+"Invalid keys %(invalid_keys)s specified.\n"
+"Valid keys are: %(valid_keys)s."
+msgstr ""
+
+#: openstackclient/common/parseractions.py:109
+#, python-format
+msgid ""
+"Missing required keys %(missing_keys)s.\n"
+"Required keys are: %(required_keys)s."
+msgstr ""
+
+#: openstackclient/compute/v2/server.py:195
+#: openstackclient/compute/v2/server.py:227
+#: openstackclient/compute/v2/server.py:598
+#: openstackclient/compute/v2/server.py:937
+#: openstackclient/compute/v2/server.py:1039
+#: openstackclient/compute/v2/server.py:1091
+#: openstackclient/compute/v2/server.py:1177
+#: openstackclient/compute/v2/server.py:1213
+#: openstackclient/compute/v2/server.py:1236
+#: openstackclient/compute/v2/server.py:1343
+#: openstackclient/compute/v2/server.py:1421
+#: openstackclient/compute/v2/server.py:1455
+#: openstackclient/compute/v2/server.py:1712
+#: openstackclient/compute/v2/server.py:1733
msgid "Server (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:188
+#: openstackclient/compute/v2/server.py:200
msgid "Security group to add (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:224
+#: openstackclient/compute/v2/server.py:232
msgid "Volume to add (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:229
+#: openstackclient/compute/v2/server.py:237
msgid "Server internal device name for volume"
msgstr ""
-#: openstackclient/compute/v2/server.py:265
-#: openstackclient/compute/v2/server.py:1341
+#: openstackclient/compute/v2/server.py:269
+#: openstackclient/compute/v2/server.py:1348
msgid "New server name"
msgstr ""
-#: openstackclient/compute/v2/server.py:273
+#: openstackclient/compute/v2/server.py:277
msgid "Create server from this image (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:278
+#: openstackclient/compute/v2/server.py:282
msgid "Create server from this volume (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:284
+#: openstackclient/compute/v2/server.py:288
msgid "Create server with this flavor (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:291
+#: openstackclient/compute/v2/server.py:295
msgid ""
-"Security group to assign to this server (name or ID) (repeat for multiple"
-" groups)"
+"Security group to assign to this server (name or ID) (repeat option to "
+"set multiple groups)"
msgstr ""
-#: openstackclient/compute/v2/server.py:297
+#: openstackclient/compute/v2/server.py:301
msgid "Keypair to inject into this server (optional extension)"
msgstr ""
-#: openstackclient/compute/v2/server.py:303
-msgid "Set a property on this server (repeat for multiple values)"
+#: openstackclient/compute/v2/server.py:307
+msgid "Set a property on this server (repeat option to set multiple values)"
msgstr ""
-#: openstackclient/compute/v2/server.py:311
-msgid "File to inject into image before boot (repeat for multiple files)"
+#: openstackclient/compute/v2/server.py:315
+msgid ""
+"File to inject into image before boot (repeat option to set multiple "
+"files)"
msgstr ""
-#: openstackclient/compute/v2/server.py:317
+#: openstackclient/compute/v2/server.py:321
msgid "User data file to serve from the metadata server"
msgstr ""
-#: openstackclient/compute/v2/server.py:322
+#: openstackclient/compute/v2/server.py:326
msgid "Select an availability zone for the server"
msgstr ""
-#: openstackclient/compute/v2/server.py:329
+#: openstackclient/compute/v2/server.py:333
msgid ""
"Map block devices; map is <id>:<type>:<size(GB)>:<delete_on_terminate> "
"(optional extension)"
msgstr ""
-#: openstackclient/compute/v2/server.py:339
+#: openstackclient/compute/v2/server.py:343
msgid ""
"Create a NIC on the server. Specify option multiple times to create "
"multiple NICs. Either net-id or port-id must be provided, but not both. "
@@ -143,296 +162,304 @@ msgid ""
"-fixed-ip: IPv6 fixed address for NIC (optional)."
msgstr ""
-#: openstackclient/compute/v2/server.py:352
+#: openstackclient/compute/v2/server.py:356
msgid "Hints for the scheduler (optional extension)"
msgstr ""
-#: openstackclient/compute/v2/server.py:358
+#: openstackclient/compute/v2/server.py:362
msgid ""
"Use specified volume as the config drive, or 'True' to use an ephemeral "
"drive"
msgstr ""
-#: openstackclient/compute/v2/server.py:366
+#: openstackclient/compute/v2/server.py:370
msgid "Minimum number of servers to launch (default=1)"
msgstr ""
-#: openstackclient/compute/v2/server.py:373
+#: openstackclient/compute/v2/server.py:377
msgid "Maximum number of servers to launch (default=1)"
msgstr ""
-#: openstackclient/compute/v2/server.py:378
+#: openstackclient/compute/v2/server.py:382
msgid "Wait for build to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:418
+#: openstackclient/compute/v2/server.py:421
msgid "min instances should be <= max instances"
msgstr ""
-#: openstackclient/compute/v2/server.py:421
+#: openstackclient/compute/v2/server.py:424
msgid "min instances should be > 0"
msgstr ""
-#: openstackclient/compute/v2/server.py:424
+#: openstackclient/compute/v2/server.py:427
msgid "max instances should be > 0"
msgstr ""
-#: openstackclient/compute/v2/server.py:453
+#: openstackclient/compute/v2/server.py:456
msgid "Volume name or ID must be specified if --block-device-mapping is specified"
msgstr ""
-#: openstackclient/compute/v2/server.py:465
+#: openstackclient/compute/v2/server.py:468
msgid "either net-id or port-id should be specified but not both"
msgstr ""
-#: openstackclient/compute/v2/server.py:485
+#: openstackclient/compute/v2/server.py:488
msgid "can't create server with port specified since network endpoint not enabled"
msgstr ""
-#: openstackclient/compute/v2/server.py:550
+#: openstackclient/compute/v2/server.py:553
#, python-format
msgid "Error creating server: %s"
msgstr ""
-#: openstackclient/compute/v2/server.py:552
+#: openstackclient/compute/v2/server.py:555
msgid ""
"\n"
"Error creating server"
msgstr ""
-#: openstackclient/compute/v2/server.py:574
+#: openstackclient/compute/v2/server.py:577
+msgid "Server(s) to create dump file (name or ID)"
+msgstr ""
+
+#: openstackclient/compute/v2/server.py:603
msgid "Name of new image (default is server name)"
msgstr ""
-#: openstackclient/compute/v2/server.py:579
+#: openstackclient/compute/v2/server.py:608
msgid "Wait for image create to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:609
+#: openstackclient/compute/v2/server.py:637
#, python-format
-msgid "Error creating server snapshot: %s"
+msgid "Error creating snapshot of server: %s"
msgstr ""
-#: openstackclient/compute/v2/server.py:611
+#: openstackclient/compute/v2/server.py:639
msgid ""
"\n"
"Error creating server snapshot"
msgstr ""
-#: openstackclient/compute/v2/server.py:633
+#: openstackclient/compute/v2/server.py:656
msgid "Server(s) to delete (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:638
+#: openstackclient/compute/v2/server.py:661
msgid "Wait for delete to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:657
+#: openstackclient/compute/v2/server.py:679
#, python-format
msgid "Error deleting server: %s"
msgstr ""
-#: openstackclient/compute/v2/server.py:659
+#: openstackclient/compute/v2/server.py:681
msgid ""
"\n"
"Error deleting server"
msgstr ""
-#: openstackclient/compute/v2/server.py:673
+#: openstackclient/compute/v2/server.py:693
msgid "Only return instances that match the reservation"
msgstr ""
-#: openstackclient/compute/v2/server.py:678
+#: openstackclient/compute/v2/server.py:698
msgid "Regular expression to match IP addresses"
msgstr ""
-#: openstackclient/compute/v2/server.py:683
+#: openstackclient/compute/v2/server.py:703
msgid "Regular expression to match IPv6 addresses"
msgstr ""
-#: openstackclient/compute/v2/server.py:688
+#: openstackclient/compute/v2/server.py:708
msgid "Regular expression to match names"
msgstr ""
-#: openstackclient/compute/v2/server.py:693
+#: openstackclient/compute/v2/server.py:713
msgid "Regular expression to match instance name (admin only)"
msgstr ""
-#: openstackclient/compute/v2/server.py:699
+#: openstackclient/compute/v2/server.py:719
msgid "Search by server status"
msgstr ""
-#: openstackclient/compute/v2/server.py:704
+#: openstackclient/compute/v2/server.py:724
msgid "Search by flavor (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:709
+#: openstackclient/compute/v2/server.py:729
msgid "Search by image (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:714
+#: openstackclient/compute/v2/server.py:734
msgid "Search by hostname"
msgstr ""
-#: openstackclient/compute/v2/server.py:720
+#: openstackclient/compute/v2/server.py:740
msgid "Include all projects (admin only)"
msgstr ""
-#: openstackclient/compute/v2/server.py:730
+#: openstackclient/compute/v2/server.py:750
msgid "Search by user (admin only) (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:888
+#: openstackclient/compute/v2/server.py:905
msgid "Server(s) to lock (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:928
+#: openstackclient/compute/v2/server.py:942
msgid "Target hostname"
msgstr ""
-#: openstackclient/compute/v2/server.py:936
+#: openstackclient/compute/v2/server.py:950
msgid "Perform a shared live migration (default)"
msgstr ""
-#: openstackclient/compute/v2/server.py:942
+#: openstackclient/compute/v2/server.py:956
msgid "Perform a block live migration"
msgstr ""
-#: openstackclient/compute/v2/server.py:949
+#: openstackclient/compute/v2/server.py:963
msgid "Allow disk over-commit on the destination host"
msgstr ""
-#: openstackclient/compute/v2/server.py:956
+#: openstackclient/compute/v2/server.py:970
msgid "Do not over-commit disk on the destination host (default)"
msgstr ""
-#: openstackclient/compute/v2/server.py:962
-#: openstackclient/compute/v2/server.py:1265
+#: openstackclient/compute/v2/server.py:976
+#: openstackclient/compute/v2/server.py:1256
msgid "Wait for resize to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:990
-#: openstackclient/compute/v2/server.py:1290
+#: openstackclient/compute/v2/server.py:1003
+#: openstackclient/compute/v2/server.py:1280
msgid "Complete\n"
msgstr ""
-#: openstackclient/compute/v2/server.py:992
+#: openstackclient/compute/v2/server.py:1005
msgid ""
"\n"
"Error migrating server"
msgstr ""
-#: openstackclient/compute/v2/server.py:1007
+#: openstackclient/compute/v2/server.py:1018
msgid "Server(s) to pause (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1040
+#: openstackclient/compute/v2/server.py:1048
msgid "Perform a hard reboot"
msgstr ""
-#: openstackclient/compute/v2/server.py:1048
+#: openstackclient/compute/v2/server.py:1056
msgid "Perform a soft reboot"
msgstr ""
-#: openstackclient/compute/v2/server.py:1053
+#: openstackclient/compute/v2/server.py:1061
msgid "Wait for reboot to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:1070
+#: openstackclient/compute/v2/server.py:1077
msgid ""
"\n"
"Reboot complete\n"
msgstr ""
-#: openstackclient/compute/v2/server.py:1072
+#: openstackclient/compute/v2/server.py:1079
msgid ""
"\n"
"Error rebooting server\n"
msgstr ""
-#: openstackclient/compute/v2/server.py:1091
+#: openstackclient/compute/v2/server.py:1096
msgid ""
"Recreate server from the specified image (name or ID). Defaults to the "
"currently used one."
msgstr ""
-#: openstackclient/compute/v2/server.py:1102
+#: openstackclient/compute/v2/server.py:1107
msgid "Wait for rebuild to complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:1124
+#: openstackclient/compute/v2/server.py:1128
msgid ""
"\n"
"Complete\n"
msgstr ""
-#: openstackclient/compute/v2/server.py:1126
+#: openstackclient/compute/v2/server.py:1130
msgid ""
"\n"
"Error rebuilding server"
msgstr ""
-#: openstackclient/compute/v2/server.py:1143
+#: openstackclient/compute/v2/server.py:1145
msgid "Name or ID of server to use"
msgstr ""
-#: openstackclient/compute/v2/server.py:1148
+#: openstackclient/compute/v2/server.py:1150
msgid "Name or ID of security group to remove from server"
msgstr ""
-#: openstackclient/compute/v2/server.py:1184
+#: openstackclient/compute/v2/server.py:1182
msgid "Volume to remove (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1250
+#: openstackclient/compute/v2/server.py:1241
msgid "Resize server to specified flavor"
msgstr ""
-#: openstackclient/compute/v2/server.py:1255
+#: openstackclient/compute/v2/server.py:1246
msgid "Confirm server resize is complete"
msgstr ""
-#: openstackclient/compute/v2/server.py:1260
+#: openstackclient/compute/v2/server.py:1251
msgid "Restore server state before resize"
msgstr ""
-#: openstackclient/compute/v2/server.py:1292
+#: openstackclient/compute/v2/server.py:1282
msgid ""
"\n"
"Error resizing server"
msgstr ""
-#: openstackclient/compute/v2/server.py:1311
+#: openstackclient/compute/v2/server.py:1299
+msgid "Server(s) to restore (name or ID)"
+msgstr ""
+
+#: openstackclient/compute/v2/server.py:1321
msgid "Server(s) to resume (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1346
+#: openstackclient/compute/v2/server.py:1353
msgid "Set new root password (interactive only)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1352
+#: openstackclient/compute/v2/server.py:1359
msgid ""
"Property to add/change for this server (repeat option to set multiple "
"properties)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1376
+#: openstackclient/compute/v2/server.py:1382
msgid "New password: "
msgstr ""
-#: openstackclient/compute/v2/server.py:1377
+#: openstackclient/compute/v2/server.py:1383
msgid "Retype new password: "
msgstr ""
-#: openstackclient/compute/v2/server.py:1381
+#: openstackclient/compute/v2/server.py:1387
msgid "Passwords do not match, password unchanged"
msgstr ""
-#: openstackclient/compute/v2/server.py:1396
+#: openstackclient/compute/v2/server.py:1400
msgid "Server(s) to shelve (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1426
+#: openstackclient/compute/v2/server.py:1427
msgid "Display server diagnostics information"
msgstr ""
@@ -440,390 +467,944 @@ msgstr ""
msgid "Error retrieving diagnostics data"
msgstr ""
-#: openstackclient/compute/v2/server.py:1462
+#: openstackclient/compute/v2/server.py:1460
msgid "Login name (ssh -l option)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1474
+#: openstackclient/compute/v2/server.py:1472
msgid "Destination port (ssh -p option)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1486
+#: openstackclient/compute/v2/server.py:1484
msgid "Private key file (ssh -i option)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1497
+#: openstackclient/compute/v2/server.py:1495
msgid "Options in ssh_config(5) format (ssh -o option)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1511
+#: openstackclient/compute/v2/server.py:1509
msgid "Use only IPv4 addresses"
msgstr ""
-#: openstackclient/compute/v2/server.py:1518
+#: openstackclient/compute/v2/server.py:1516
msgid "Use only IPv6 addresses"
msgstr ""
-#: openstackclient/compute/v2/server.py:1527
+#: openstackclient/compute/v2/server.py:1525
msgid "Use public IP address"
msgstr ""
-#: openstackclient/compute/v2/server.py:1535
+#: openstackclient/compute/v2/server.py:1533
msgid "Use private IP address"
msgstr ""
-#: openstackclient/compute/v2/server.py:1542
+#: openstackclient/compute/v2/server.py:1540
msgid "Use other IP address (public, private, etc)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1605
+#: openstackclient/compute/v2/server.py:1600
msgid "Server(s) to start (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1630
+#: openstackclient/compute/v2/server.py:1622
msgid "Server(s) to stop (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1655
+#: openstackclient/compute/v2/server.py:1644
msgid "Server(s) to suspend (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1681
+#: openstackclient/compute/v2/server.py:1667
msgid "Server(s) to unlock (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1707
+#: openstackclient/compute/v2/server.py:1690
msgid "Server(s) to unpause (name or ID)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1763
-msgid "Property key to remove from server (repeat to unset multiple values)"
+#: openstackclient/compute/v2/server.py:1740
+msgid ""
+"Property key to remove from server (repeat option to remove multiple "
+"values)"
msgstr ""
-#: openstackclient/compute/v2/server.py:1794
+#: openstackclient/compute/v2/server.py:1768
msgid "Server(s) to unshelve (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/catalog.py:75
-#: openstackclient/identity/v3/catalog.py:72
+#: openstackclient/compute/v2/service.py:138
+msgid "argument --disable-reason has been ignored"
+msgstr ""
+
+#: openstackclient/identity/v2_0/catalog.py:67
+#: openstackclient/identity/v3/catalog.py:64
msgid "Service to display (type or name)"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:40
-#: openstackclient/identity/v3/ec2creds.py:65
+#: openstackclient/identity/v2_0/ec2creds.py:34
+#: openstackclient/identity/v3/ec2creds.py:59
msgid ""
"Create credentials in project (name or ID; default: current authenticated"
" project)"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:48
-#: openstackclient/identity/v3/ec2creds.py:73
+#: openstackclient/identity/v2_0/ec2creds.py:42
+#: openstackclient/identity/v3/ec2creds.py:67
msgid ""
"Create credentials for user (name or ID; default: current authenticated "
"user)"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:99
-#: openstackclient/identity/v2_0/ec2creds.py:172
-#: openstackclient/identity/v3/ec2creds.py:129
-#: openstackclient/identity/v3/ec2creds.py:187
+#: openstackclient/identity/v2_0/ec2creds.py:90
+#: openstackclient/identity/v2_0/ec2creds.py:157
+#: openstackclient/identity/v3/ec2creds.py:120
+#: openstackclient/identity/v3/ec2creds.py:172
msgid "Credentials access key"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:104
-#: openstackclient/identity/v3/ec2creds.py:134
+#: openstackclient/identity/v2_0/ec2creds.py:95
+#: openstackclient/identity/v3/ec2creds.py:125
msgid "Delete credentials for user (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:134
-#: openstackclient/identity/v3/ec2creds.py:156
+#: openstackclient/identity/v2_0/ec2creds.py:122
+#: openstackclient/identity/v3/ec2creds.py:144
msgid "Filter list by user (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/ec2creds.py:177
-#: openstackclient/identity/v3/ec2creds.py:192
+#: openstackclient/identity/v2_0/ec2creds.py:162
+#: openstackclient/identity/v3/ec2creds.py:177
msgid "Show credentials for user (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:40
+#: openstackclient/identity/v2_0/endpoint.py:34
msgid "New endpoint service (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:46
+#: openstackclient/identity/v2_0/endpoint.py:40
msgid "New endpoint public URL (required)"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:51
+#: openstackclient/identity/v2_0/endpoint.py:45
msgid "New endpoint admin URL"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:56
+#: openstackclient/identity/v2_0/endpoint.py:50
msgid "New endpoint internal URL"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:61
+#: openstackclient/identity/v2_0/endpoint.py:55
msgid "New endpoint region ID"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:93
+#: openstackclient/identity/v2_0/endpoint.py:84
msgid "Endpoint ID to delete"
msgstr ""
-#: openstackclient/identity/v2_0/endpoint.py:149
+#: openstackclient/identity/v2_0/endpoint.py:133
msgid "Endpoint ID to display"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:41
+#: openstackclient/identity/v2_0/project.py:36
msgid "New project name"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:46
+#: openstackclient/identity/v2_0/project.py:41
msgid "Project description"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:52
+#: openstackclient/identity/v2_0/project.py:47
msgid "Enable project (default)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:57
-#: openstackclient/identity/v2_0/project.py:194
+#: openstackclient/identity/v2_0/project.py:52
+#: openstackclient/identity/v2_0/project.py:179
msgid "Disable project"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:63
+#: openstackclient/identity/v2_0/project.py:58
msgid "Add a property to <name> (repeat option to set multiple properties)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:69
-#: openstackclient/identity/v3/project.py:80
+#: openstackclient/identity/v2_0/project.py:64
+#: openstackclient/identity/v3/project.py:75
msgid "Return existing project"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:117
+#: openstackclient/identity/v2_0/project.py:109
msgid "Project(s) to delete (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:173
-#: openstackclient/identity/v2_0/project.py:313
+#: openstackclient/identity/v2_0/project.py:158
+#: openstackclient/identity/v2_0/project.py:291
msgid "Project to modify (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:178
+#: openstackclient/identity/v2_0/project.py:163
msgid "Set project name"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:183
+#: openstackclient/identity/v2_0/project.py:168
msgid "Set project description"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:189
+#: openstackclient/identity/v2_0/project.py:174
msgid "Enable project"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:200
+#: openstackclient/identity/v2_0/project.py:185
msgid "Set a project property (repeat option to set multiple properties)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:253
+#: openstackclient/identity/v2_0/project.py:234
msgid "Project to display (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/project.py:320
+#: openstackclient/identity/v2_0/project.py:298
msgid "Unset a project property (repeat option to unset multiple properties)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:41
+#: openstackclient/identity/v2_0/role.py:36
msgid "Role to add to <project>:<user> (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:47
-#: openstackclient/identity/v2_0/role.py:309
+#: openstackclient/identity/v2_0/role.py:42
+#: openstackclient/identity/v2_0/role.py:288
msgid "Include <project> (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:53
-#: openstackclient/identity/v2_0/role.py:315
+#: openstackclient/identity/v2_0/role.py:48
+#: openstackclient/identity/v2_0/role.py:294
msgid "Include <user> (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:87
+#: openstackclient/identity/v2_0/role.py:79
msgid "New role name"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:92
-#: openstackclient/identity/v3/role.py:165
+#: openstackclient/identity/v2_0/role.py:84
+#: openstackclient/identity/v3/role.py:155
msgid "Return existing role"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:127
+#: openstackclient/identity/v2_0/role.py:116
msgid "Role(s) to delete (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:194
-#: openstackclient/identity/v2_0/role.py:257
+#: openstackclient/identity/v2_0/role.py:178
+#: openstackclient/identity/v2_0/role.py:238
msgid "Project must be specified"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:208
-#: openstackclient/identity/v2_0/role.py:263
+#: openstackclient/identity/v2_0/role.py:192
+#: openstackclient/identity/v2_0/role.py:244
msgid "User must be specified"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:236
+#: openstackclient/identity/v2_0/role.py:218
msgid "User to list (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:241
+#: openstackclient/identity/v2_0/role.py:223
msgid "Filter users by <project> (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:303
+#: openstackclient/identity/v2_0/role.py:282
msgid "Role to remove (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/role.py:344
+#: openstackclient/identity/v2_0/role.py:320
msgid "Role to display (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:42
+#: openstackclient/identity/v2_0/service.py:36
msgid "New service type (compute, image, identity, volume, etc)"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:53
+#: openstackclient/identity/v2_0/service.py:47
msgid "New service name"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:58
+#: openstackclient/identity/v2_0/service.py:52
msgid "New service description"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:78
+#: openstackclient/identity/v2_0/service.py:71
msgid ""
"The argument --type is deprecated, use service create --name <service-"
"name> type instead."
msgstr ""
-#: openstackclient/identity/v2_0/service.py:105
+#: openstackclient/identity/v2_0/service.py:96
msgid "Service to delete (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:156
+#: openstackclient/identity/v2_0/service.py:140
msgid "Service to display (type, name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:162
+#: openstackclient/identity/v2_0/service.py:146
msgid "Show service catalog information"
msgstr ""
-#: openstackclient/identity/v2_0/service.py:180
+#: openstackclient/identity/v2_0/service.py:163
#, python-format
msgid "No service catalog with a type, name or ID of '%s' exists."
msgstr ""
-#: openstackclient/identity/v2_0/token.py:55
+#: openstackclient/identity/v2_0/token.py:50
msgid "Token to be deleted"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:40
+#: openstackclient/identity/v2_0/user.py:35
msgid "New user name"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:45
+#: openstackclient/identity/v2_0/user.py:40
msgid "Default project (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:50
-#: openstackclient/identity/v2_0/user.py:273
+#: openstackclient/identity/v2_0/user.py:45
+#: openstackclient/identity/v2_0/user.py:258
msgid "Set user password"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:56
-#: openstackclient/identity/v2_0/user.py:279
+#: openstackclient/identity/v2_0/user.py:51
+#: openstackclient/identity/v2_0/user.py:264
msgid "Prompt interactively for password"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:61
-#: openstackclient/identity/v2_0/user.py:284
+#: openstackclient/identity/v2_0/user.py:56
+#: openstackclient/identity/v2_0/user.py:269
msgid "Set user email address"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:67
-#: openstackclient/identity/v2_0/user.py:290
+#: openstackclient/identity/v2_0/user.py:62
+#: openstackclient/identity/v2_0/user.py:275
msgid "Enable user (default)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:72
-#: openstackclient/identity/v2_0/user.py:295
+#: openstackclient/identity/v2_0/user.py:67
+#: openstackclient/identity/v2_0/user.py:280
msgid "Disable user"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:77
-#: openstackclient/identity/v3/user.py:90
+#: openstackclient/identity/v2_0/user.py:72
+#: openstackclient/identity/v3/user.py:86
msgid "Return existing user"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:141
+#: openstackclient/identity/v2_0/user.py:133
msgid "User(s) to delete (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:168
+#: openstackclient/identity/v2_0/user.py:156
msgid "Filter users by project (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:258
+#: openstackclient/identity/v2_0/user.py:243
msgid "User to change (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:263
+#: openstackclient/identity/v2_0/user.py:248
msgid "Set user name"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:268
+#: openstackclient/identity/v2_0/user.py:253
msgid "Set default project (name or ID)"
msgstr ""
-#: openstackclient/identity/v2_0/user.py:361
+#: openstackclient/identity/v2_0/user.py:342
msgid "User to display (name or ID)"
msgstr ""
-#: openstackclient/identity/v3/domain.py:62
+#: openstackclient/identity/v3/domain.py:57
msgid "Return existing domain"
msgstr ""
-#: openstackclient/identity/v3/group.py:141
+#: openstackclient/identity/v3/group.py:130
msgid "Return existing group"
msgstr ""
-#: openstackclient/identity/v3/region.py:39
+#: openstackclient/identity/v3/region.py:33
msgid "New region ID"
msgstr ""
-#: openstackclient/identity/v3/region.py:44
+#: openstackclient/identity/v3/region.py:38
msgid "Parent region ID"
msgstr ""
-#: openstackclient/identity/v3/region.py:49
-#: openstackclient/identity/v3/region.py:144
+#: openstackclient/identity/v3/region.py:43
+#: openstackclient/identity/v3/region.py:128
msgid "New region description"
msgstr ""
-#: openstackclient/identity/v3/region.py:79
+#: openstackclient/identity/v3/region.py:70
msgid "Region ID to delete"
msgstr ""
-#: openstackclient/identity/v3/region.py:101
+#: openstackclient/identity/v3/region.py:88
msgid "Filter by parent region ID"
msgstr ""
-#: openstackclient/identity/v3/region.py:134
+#: openstackclient/identity/v3/region.py:118
msgid "Region to modify"
msgstr ""
-#: openstackclient/identity/v3/region.py:139
+#: openstackclient/identity/v3/region.py:123
msgid "New parent region ID"
msgstr ""
-#: openstackclient/identity/v3/region.py:175
+#: openstackclient/identity/v3/region.py:154
msgid "Region to display"
msgstr ""
+#: openstackclient/image/v1/image.py:191 openstackclient/image/v1/image.py:609
+#: openstackclient/image/v2/image.py:283 openstackclient/image/v2/image.py:794
+msgid "The --owner option is deprecated, please use --project instead."
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:67
+msgid "Network to allocate floating IP from (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:75
+msgid "Subnet on which you want to create the floating IP (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:81
+msgid "Port to be associated with the floating IP (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:88
+msgid "Floating IP address"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:94
+msgid "Fixed IP address mapped to the floating IP"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:119
+msgid "Floating IP to delete (IP address or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/floating_ip.py:193
+msgid "Floating IP to display (IP address or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:114
+msgid "New network name"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:121
+#: openstackclient/network/v2/network.py:385
+msgid "Share the network between projects"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:126
+#: openstackclient/network/v2/network.py:390
+msgid "Do not share the network between projects"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:136
+msgid "Enable network (default)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:141
+#: openstackclient/network/v2/network.py:378
+msgid "Disable network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:146
+#: openstackclient/network/v2/port.py:246
+#: openstackclient/network/v2/router.py:174
+#: openstackclient/network/v2/security_group.py:116
+#: openstackclient/network/v2/security_group_rule.py:127
+#: openstackclient/network/v2/subnet.py:183
+#: openstackclient/network/v2/subnet_pool.py:114
+msgid "Owner's project (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:154
+msgid ""
+"Availability Zone in which to create this network (Network Availability "
+"Zone extension required, repeat option to set multiple availability "
+"zones)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:162
+#: openstackclient/network/v2/network.py:396
+msgid "Set this network as an external network (external-net extension required)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:168
+msgid "Set this network as an internal network (default)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:174
+msgid "Specify if this network should be used as the default external network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:180
+msgid "Do not use the network as the default external network. (default)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:188
+msgid ""
+"The physical mechanism by which the virtual network is implemented. The "
+"supported options are: flat, gre, local, vlan, vxlan"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:196
+msgid "Name of the physical network over which the virtual network is implemented"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:203
+msgid "VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN networks"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:212
+msgid "IPv4 subnet for fixed IPs (in CIDR notation)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:361
+msgid "Network to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:366
+msgid "Set network name"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:373
+msgid "Enable network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:402
+msgid "Set this network as an internal network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:408
+msgid "Set the network as the default external network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:413
+msgid "Do not use the network as the default external network"
+msgstr ""
+
+#: openstackclient/network/v2/network.py:436
+msgid "Network to display (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:73
+msgid "The --device-id option is deprecated, please use --device instead."
+msgstr ""
+
+#: openstackclient/network/v2/port.py:79
+msgid "The --host-id option is deprecated, please use --host instead."
+msgstr ""
+
+#: openstackclient/network/v2/port.py:161
+msgid "Port device ID"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:171
+msgid "Device owner of this port"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:178
+msgid ""
+"VNIC type for this port (direct | direct-physical | macvtap | normal | "
+"baremetal, default: normal)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:187
+msgid "Allocate port on host <host-id> (ID only)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:206
+msgid "Network this port belongs to (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:214
+#: openstackclient/network/v2/port.py:365
+msgid ""
+"Desired IP and/or subnet (name or ID) for this port: subnet=<subnet>,ip-"
+"address=<ip-address> (repeat option to set multiple fixed IP addresses)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:222
+#: openstackclient/network/v2/port.py:379
+msgid ""
+"Custom data to be passed as binding:profile: <key>=<value> (repeat option"
+" to set multiple binding:profile data)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:231
+msgid "Enable port (default)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:236
+#: openstackclient/network/v2/port.py:352
+msgid "Disable port"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:241
+msgid "MAC address of this port"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:280
+msgid "Port(s) to delete (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:301
+msgid "List only ports attached to this router (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:347
+msgid "Enable port"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:357
+msgid "Set port name"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:372
+msgid "Clear existing information of fixed IP addresses"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:386
+msgid "Clear existing information of binding:profile"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:391
+msgid "Port to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/port.py:429
+msgid "Port to display (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:99
+msgid "Router to which port will be added (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:104
+msgid "Port to be added (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:123
+msgid "Router to which subnet will be added (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:128
+msgid "Subnet to be added (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:150
+msgid "New router name"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:157
+msgid "Enable router (default)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:162
+#: openstackclient/network/v2/router.py:351
+msgid "Disable router"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:169
+msgid "Create a distributed router"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:182
+msgid ""
+"Availability Zone in which to create this router (Router Availability "
+"Zone extension required, repeat option to set multiple availability "
+"zones)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:210
+msgid "Router(s) to delete (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:283
+msgid "Router from which port will be removed (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:288
+msgid "Port to be removed (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:307
+msgid "Router from which the subnet will be removed (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:312
+msgid "Subnet to be removed (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:334
+msgid "Router to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:339
+msgid "Set router name"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:346
+msgid "Enable router"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:357
+msgid "Set router to distributed mode (disabled router only)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:362
+msgid "Set router to centralized mode (disabled router only)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:372
+msgid ""
+"Routes associated with the router destination: destination subnet (in "
+"CIDR notation) gateway: nexthop IP address (repeat option to set multiple"
+" routes)"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:380
+msgid "Clear routes associated with the router"
+msgstr ""
+
+#: openstackclient/network/v2/router.py:412
+msgid "Router to display (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:103
+#: openstackclient/network/v2/security_group.py:249
+msgid "New security group name"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:108
+msgid "Security group description"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:173
+msgid "Security group to delete (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:208
+msgid "Display information from all projects (admin only)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:244
+msgid "Security group to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:254
+msgid "New security group description"
+msgstr ""
+
+#: openstackclient/network/v2/security_group.py:299
+msgid "Security group to display (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:82
+msgid "IP protocol (icmp, tcp, udp; default: tcp)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:88
+msgid ""
+"Source IP address block (may use CIDR notation; default for IPv4 rule: "
+"0.0.0.0/0)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:94
+msgid "Source security group (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:101
+msgid ""
+"Destination port, may be a single port or port range: 137:139 (only "
+"required for IP protocols tcp and udp)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:111
+msgid "Rule applies to incoming network traffic (default)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:116
+msgid "Rule applies to outgoing network traffic"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:122
+msgid "Ethertype of network traffic (IPv4, IPv6; default: IPv4)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:221
+msgid "Security group rule to delete (ID only)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:241
+msgid "List all rules in this security group (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/security_group_rule.py:336
+msgid "Security group rule to display (ID only)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:54
+msgid ""
+"Allocation pool IP addresses for this subnet e.g.: "
+"start=192.168.199.2,end=192.168.199.254 (repeat option to add multiple IP"
+" addresses)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:63
+msgid "DNS server for this subnet (repeat option to set multiple DNS servers)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:72
+msgid ""
+"Additional route for this subnet e.g.: "
+"destination=10.10.0.0/16,gateway=192.168.71.254 destination: destination "
+"subnet (in CIDR notation) gateway: nexthop IP address (repeat option to "
+"add multiple routes)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:178
+msgid "New subnet name"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:190
+msgid "Subnet pool from which this subnet will obtain a CIDR (Name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:196
+msgid "Use default subnet pool for --ip-version"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:201
+msgid "Prefix length for subnet allocation from subnet pool"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:206
+msgid ""
+"Subnet range in CIDR notation (required if --subnet-pool is not "
+"specified, optional otherwise)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:215
+msgid "Enable DHCP (default)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:220
+#: openstackclient/network/v2/subnet.py:348
+msgid "Disable DHCP"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:226
+msgid ""
+"Specify a gateway for the subnet. The three options are: <ip-address>: "
+"Specific IP address to use as the gateway, 'auto': Gateway address should"
+" automatically be chosen from within the subnet itself, 'none': This "
+"subnet will not use a gateway, e.g.: --gateway 192.168.9.1, --gateway "
+"auto, --gateway none (default is 'auto')"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:238
+msgid ""
+"IP version (default is 4). Note that when subnet pool is specified, IP "
+"version is determined from the subnet pool and this option is ignored"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:245
+msgid ""
+"IPv6 RA (Router Advertisement) mode, valid modes: [dhcpv6-stateful, "
+"dhcpv6-stateless, slaac]"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:251
+msgid "IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:258
+msgid "Network this subnet belongs to (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:280
+msgid "Subnet to delete (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:331
+msgid "Subnet to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:336
+msgid "Updated name of the subnet"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:343
+msgid "Enable DHCP"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:353
+msgid ""
+"Specify a gateway for the subnet. The options are: <ip-address>: Specific"
+" IP address to use as the gateway, 'none': This subnet will not use a "
+"gateway, e.g.: --gateway 192.168.9.1, --gateway none"
+msgstr ""
+
+#: openstackclient/network/v2/subnet.py:387
+msgid "Subnet to display (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:77
+msgid ""
+"Set subnet pool prefixes (in CIDR notation) (repeat option to set "
+"multiple prefixes)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:84
+msgid "Set subnet pool default prefix length"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:90
+msgid "Set subnet pool minimum prefix length"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:96
+msgid "Set subnet pool maximum prefix length"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:108
+msgid "Name of the new subnet pool"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:120
+#: openstackclient/network/v2/subnet_pool.py:226
+msgid ""
+"Set address scope associated with the subnet pool (name or ID), prefixes "
+"must be unique across address scopes"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:146
+msgid "Subnet pool to delete (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:214
+msgid "Subnet pool to modify (name or ID)"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:219
+msgid "Set subnet pool name"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:233
+msgid "Remove address scope associated with the subnet pool"
+msgstr ""
+
+#: openstackclient/network/v2/subnet_pool.py:262
+msgid "Subnet pool to display (name or ID)"
+msgstr ""
+
diff --git a/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po b/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po
index 26032666..a121acab 100644
--- a/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po
+++ b/openstackclient/locale/zh_TW/LC_MESSAGES/openstackclient.po
@@ -4,12 +4,12 @@
# python-openstackclient project.
#
# Translators:
-# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
+# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
msgid ""
msgstr ""
-"Project-Id-Version: python-openstackclient 2.0.1.dev168\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2016-01-19 02:20+0000\n"
+"Project-Id-Version: python-openstackclient 2.2.1.dev235\n"
+"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
+"POT-Creation-Date: 2016-04-19 05:02+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -126,19 +126,12 @@ msgid "Endpoint ID to display"
msgstr "要顯示的端點識別號"
#, python-format
-msgid "Error creating server snapshot: %s"
-msgstr "新增雲實例即時存檔時出錯:%s"
-
-#, python-format
msgid "Error creating server: %s"
msgstr "新增雲實例時出錯:%s"
msgid "Error retrieving diagnostics data"
msgstr "獲得診斷資料時出錯"
-msgid "File to inject into image before boot (repeat for multiple files)"
-msgstr "在開機前要注入映像檔的檔案(為多個檔案重復指定)"
-
msgid "Filter by parent region ID"
msgstr "以父地區識別號來篩選"
@@ -287,9 +280,6 @@ msgstr "要刪除的專案(名稱或識別號)"
msgid "Prompt interactively for password"
msgstr "為密碼互動提示"
-msgid "Property key to remove from server (repeat to unset multiple values)"
-msgstr "要從雲實例上移除的屬性鍵(重復來取消選擇多個值)"
-
msgid ""
"Property to add/change for this server (repeat option to set multiple "
"properties)"
@@ -385,9 +375,6 @@ msgstr "要顯示的伺服器(類型、名稱或識別號)"
msgid "Set a project property (repeat option to set multiple properties)"
msgstr "設定專案屬性(重復這選項來設定多個屬性)"
-msgid "Set a property on this server (repeat for multiple values)"
-msgstr "為此伺服器設定屬性(為多個值重複設定)"
-
msgid "Set default project (name or ID)"
msgstr "設定預設專案(名稱或識別號)"
diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py
index dca9efc4..be06d2b5 100644
--- a/openstackclient/network/client.py
+++ b/openstackclient/network/client.py
@@ -14,6 +14,7 @@
import logging
from openstack import connection
+from openstack import profile
from openstackclient.common import utils
@@ -31,8 +32,13 @@ API_VERSIONS = {
def make_client(instance):
"""Returns a network proxy"""
+ prof = profile.Profile()
+ prof.set_region(API_NAME, instance._region_name)
+ prof.set_version(API_NAME, instance._api_version[API_NAME])
conn = connection.Connection(authenticator=instance.session.auth,
- verify=instance.session.verify)
+ verify=instance.session.verify,
+ cert=instance.session.cert,
+ profile=prof)
LOG.debug('Connection: %s', conn)
LOG.debug('Network client initialized using OpenStack SDK: %s',
conn.network)
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index d57a1ed6..4b77971a 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -86,10 +86,40 @@ def _get_attrs(client_manager, parsed_args):
attrs['is_default'] = False
if parsed_args.default:
attrs['is_default'] = True
-
+ # Update Provider network options
+ if parsed_args.provider_network_type:
+ attrs['provider:network_type'] = parsed_args.provider_network_type
+ if parsed_args.physical_network:
+ attrs['provider:physical_network'] = parsed_args.physical_network
+ if parsed_args.segmentation_id:
+ attrs['provider:segmentation_id'] = parsed_args.segmentation_id
return attrs
+def _add_provider_network_options(parser):
+ # Add provider network options
+ parser.add_argument(
+ '--provider-network-type',
+ metavar='<provider-network-type>',
+ choices=['flat', 'gre', 'local',
+ 'vlan', 'vxlan'],
+ help=_("The physical mechanism by which the virtual network "
+ "is implemented. The supported options are: "
+ "flat, gre, local, vlan, vxlan"))
+ parser.add_argument(
+ '--provider-physical-network',
+ metavar='<provider-physical-network>',
+ dest='physical_network',
+ help=_("Name of the physical network over which the virtual "
+ "network is implemented"))
+ parser.add_argument(
+ '--provider-segment',
+ metavar='<provider-segment>',
+ dest='segmentation_id',
+ help=_("VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN "
+ "networks"))
+
+
def _get_attrs_compute(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
@@ -100,7 +130,6 @@ def _get_attrs_compute(client_manager, parsed_args):
attrs['share_address'] = False
if parsed_args.subnet is not None:
attrs['cidr'] = parsed_args.subnet
-
return attrs
@@ -180,29 +209,7 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
help=_("Do not use the network as the default external network. "
"(default)")
)
- parser.add_argument(
- '--provider-network-type',
- metavar='<provider-network-type>',
- choices=['flat', 'gre', 'local',
- 'vlan', 'vxlan'],
- help=_("The physical mechanism by which the virtual network "
- "is implemented. The supported options are: "
- "flat, gre, local, vlan, vxlan")
- )
- parser.add_argument(
- '--provider-physical-network',
- metavar='<provider-physical-network>',
- dest='physical_network',
- help=_("Name of the physical network over which the virtual "
- "network is implemented")
- )
- parser.add_argument(
- '--provider-segment',
- metavar='<provider-segment>',
- dest='segmentation_id',
- help=_("VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN "
- "networks")
- )
+ _add_provider_network_options(parser)
return parser
def update_parser_compute(self, parser):
@@ -215,12 +222,6 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args):
attrs = _get_attrs(self.app.client_manager, parsed_args)
- if parsed_args.provider_network_type:
- attrs['provider:network_type'] = parsed_args.provider_network_type
- if parsed_args.physical_network:
- attrs['provider:physical_network'] = parsed_args.physical_network
- if parsed_args.segmentation_id:
- attrs['provider:segmentation_id'] = parsed_args.segmentation_id
obj = client.create_network(**attrs)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
@@ -412,6 +413,7 @@ class SetNetwork(command.Command):
action='store_true',
help=_("Do not use the network as the default external network")
)
+ _add_provider_network_options(parser)
return parser
def take_action(self, parsed_args):
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index fbfce4d3..9b6161fd 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -249,7 +249,8 @@ class CreatePort(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='Name of this port')
+ help=_("Name of this port")
+ )
# TODO(singhj): Add support for extended options:
# qos,security groups,dhcp, address pairs
return parser
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 67472de0..5b22a0dd 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -13,6 +13,7 @@
"""Security Group Rule action implementations"""
+import argparse
import six
try:
@@ -242,14 +243,50 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
)
return parser
+ def update_parser_network(self, parser):
+ # Accept but hide the argument for consistency with compute.
+ # Network will always return all projects for an admin.
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_("List additional fields in output")
+ )
+ return parser
+
+ def update_parser_compute(self, parser):
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_("Display information from all projects (admin only)")
+ )
+ # Accept but hide the argument for consistency with network.
+ # There are no additional fields to display at this time.
+ parser.add_argument(
+ '--long',
+ action='store_false',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ return parser
+
def _get_column_headers(self, parsed_args):
column_headers = (
'ID',
'IP Protocol',
'IP Range',
'Port Range',
- 'Remote Security Group',
)
+ if parsed_args.long:
+ column_headers = column_headers + ('Direction', 'Ethertype',)
+ column_headers = column_headers + ('Remote Security Group',)
if parsed_args.group is None:
column_headers = column_headers + ('Security Group',)
return column_headers
@@ -261,8 +298,10 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
'protocol',
'remote_ip_prefix',
'port_range_min',
- 'remote_group_id',
)
+ if parsed_args.long:
+ columns = columns + ('direction', 'ethertype',)
+ columns = columns + ('remote_group_id',)
# Get the security group rules using the requested query.
query = {}
@@ -309,7 +348,8 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
rules_to_list = group.rules
else:
columns = columns + ('parent_group_id',)
- for group in client.security_groups.list():
+ search = {'all_tenants': parsed_args.all_projects}
+ for group in client.security_groups.list(search_opts=search):
rules_to_list.extend(group.rules)
# NOTE(rtheis): Turn the raw rules into resources.
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 715e6620..fb441cbf 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -14,8 +14,6 @@
"""Subnet action implementations"""
import copy
-from json.encoder import JSONEncoder
-
from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions
@@ -31,10 +29,8 @@ def _format_allocation_pools(data):
def _format_host_routes(data):
- try:
- return '\n'.join([JSONEncoder().encode(route) for route in data])
- except (TypeError, KeyError):
- return ''
+ # Map the host route keys to match --host-route option.
+ return utils.format_list_of_dicts(convert_entries_to_gateway(data))
_formatters = {
@@ -89,8 +85,9 @@ def convert_entries_to_nexthop(entries):
# Change 'gateway' entry to 'nexthop'
changed_entries = copy.deepcopy(entries)
for entry in changed_entries:
- entry['nexthop'] = entry['gateway']
- del entry['gateway']
+ if 'gateway' in entry:
+ entry['nexthop'] = entry['gateway']
+ del entry['gateway']
return changed_entries
@@ -99,8 +96,9 @@ def convert_entries_to_gateway(entries):
# Change 'nexthop' entry to 'gateway'
changed_entries = copy.deepcopy(entries)
for entry in changed_entries:
- entry['gateway'] = entry['nexthop']
- del entry['nexthop']
+ if 'nexthop' in entry:
+ entry['gateway'] = entry['nexthop']
+ del entry['nexthop']
return changed_entries
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 482b5ecf..f1174dda 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -55,6 +55,16 @@ def _get_attrs(client_manager, parsed_args):
if 'no_address_scope' in parsed_args and parsed_args.no_address_scope:
attrs['address_scope_id'] = None
+ if parsed_args.default:
+ attrs['is_default'] = True
+ if parsed_args.no_default:
+ attrs['is_default'] = False
+
+ if 'share' in parsed_args and parsed_args.share:
+ attrs['shared'] = True
+ if 'no_share' in parsed_args and parsed_args.no_share:
+ attrs['shared'] = False
+
# "subnet pool set" command doesn't support setting project.
if 'project' in parsed_args and parsed_args.project is not None:
identity_client = client_manager.identity
@@ -68,18 +78,20 @@ def _get_attrs(client_manager, parsed_args):
return attrs
-def _add_prefix_options(parser):
+def _add_prefix_options(parser, for_create=False):
parser.add_argument(
'--pool-prefix',
metavar='<pool-prefix>',
dest='prefixes',
action='append',
+ required=for_create,
help=_("Set subnet pool prefixes (in CIDR notation) "
"(repeat option to set multiple prefixes)")
)
parser.add_argument(
'--default-prefix-length',
metavar='<default-prefix-length>',
+ type=int,
action=parseractions.NonNegativeAction,
help=_("Set subnet pool default prefix length")
)
@@ -87,16 +99,32 @@ def _add_prefix_options(parser):
'--min-prefix-length',
metavar='<min-prefix-length>',
action=parseractions.NonNegativeAction,
+ type=int,
help=_("Set subnet pool minimum prefix length")
)
parser.add_argument(
'--max-prefix-length',
metavar='<max-prefix-length>',
+ type=int,
action=parseractions.NonNegativeAction,
help=_("Set subnet pool maximum prefix length")
)
+def _add_default_options(parser):
+ default_group = parser.add_mutually_exclusive_group()
+ default_group.add_argument(
+ '--default',
+ action='store_true',
+ help=_("Set this as a default subnet pool"),
+ )
+ default_group.add_argument(
+ '--no-default',
+ action='store_true',
+ help=_("Set this as a non-default subnet pool"),
+ )
+
+
class CreateSubnetPool(command.ShowOne):
"""Create subnet pool"""
@@ -107,7 +135,7 @@ class CreateSubnetPool(command.ShowOne):
metavar='<name>',
help=_("Name of the new subnet pool")
)
- _add_prefix_options(parser)
+ _add_prefix_options(parser, for_create=True)
parser.add_argument(
'--project',
metavar='<project>',
@@ -121,6 +149,18 @@ class CreateSubnetPool(command.ShowOne):
"(name or ID), prefixes must be unique across address "
"scopes")
)
+ _add_default_options(parser)
+ shared_group = parser.add_mutually_exclusive_group()
+ shared_group.add_argument(
+ '--share',
+ action='store_true',
+ help=_("Set this subnet pool as shared"),
+ )
+ shared_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_("Set this subnet pool as not shared"),
+ )
return parser
def take_action(self, parsed_args):
@@ -176,6 +216,8 @@ class ListSubnetPool(command.Lister):
'Prefixes',
'Default Prefix Length',
'Address Scope',
+ 'Default Subnet Pool',
+ 'Shared',
)
columns = (
'id',
@@ -183,6 +225,8 @@ class ListSubnetPool(command.Lister):
'prefixes',
'default_prefixlen',
'address_scope_id',
+ 'is_default',
+ 'shared',
)
else:
headers = (
@@ -232,6 +276,8 @@ class SetSubnetPool(command.Command):
action='store_true',
help=_("Remove address scope associated with the subnet pool")
)
+ _add_default_options(parser)
+
return parser
def take_action(self, parsed_args):
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index b7bc7b1a..b96fb089 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -16,6 +16,7 @@
"""Command-line interface to the OpenStack APIs"""
+import argparse
import getpass
import logging
import sys
@@ -131,6 +132,16 @@ class OpenStackShell(app.App):
self.log.info("END return value: %s", ret_val)
def init_profile(self):
+ # NOTE(dtroyer): Remove this 'if' block when the --profile global
+ # option is removed
+ if osprofiler_profiler and self.options.old_profile:
+ self.log.warning(
+ 'The --profile option is deprecated, '
+ 'please use --os-profile instead'
+ )
+ if not self.options.profile:
+ self.options.profile = self.options.old_profile
+
self.do_profile = osprofiler_profiler and self.options.profile
if self.do_profile:
osprofiler_profiler.init(self.options.profile)
@@ -144,7 +155,7 @@ class OpenStackShell(app.App):
# bigger than most big default one (CRITICAL) or something like
# that (PROFILE = 60 for instance), but not sure we need it here.
self.log.warning("Trace ID: %s" % trace_id)
- self.log.warning("To display trace use next command:\n"
+ self.log.warning("Display trace with command:\n"
"osprofiler trace show --html %s " % trace_id)
def run_subcommand(self, argv):
@@ -242,16 +253,22 @@ class OpenStackShell(app.App):
# osprofiler HMAC key argument
if osprofiler_profiler:
- parser.add_argument('--profile',
- metavar='hmac-key',
- help='HMAC key to use for encrypting context '
- 'data for performance profiling of operation. '
- 'This key should be the value of one of the '
- 'HMAC keys configured in osprofiler '
- 'middleware in the projects user would like '
- 'to profile. It needs to be specified in '
- 'configuration files of the required '
- 'projects.')
+ parser.add_argument(
+ '--os-profile',
+ metavar='hmac-key',
+ dest='profile',
+ help='HMAC key for encrypting profiling context data',
+ )
+ # NOTE(dtroyer): This global option should have been named
+ # --os-profile as --profile interferes with at
+ # least one existing command option. Deprecate
+ # --profile and remove after Apr 2017.
+ parser.add_argument(
+ '--profile',
+ metavar='hmac-key',
+ dest='old_profile',
+ help=argparse.SUPPRESS,
+ )
return clientmanager.build_plugin_option_parser(parser)
diff --git a/openstackclient/tests/common/test_quota.py b/openstackclient/tests/common/test_quota.py
index edf29c9b..ba7ee469 100644
--- a/openstackclient/tests/common/test_quota.py
+++ b/openstackclient/tests/common/test_quota.py
@@ -59,6 +59,7 @@ class TestQuota(compute_fakes.TestComputev2):
self.service_catalog_mock = \
self.app.client_manager.auth_ref.service_catalog
self.service_catalog_mock.reset_mock()
+ self.app.client_manager.auth_ref.project_id = identity_fakes.project_id
class TestQuotaSet(TestQuota):
@@ -304,3 +305,13 @@ class TestQuotaShow(TestQuota):
identity_fakes.project_id)
self.volume_quotas_class_mock.get.assert_called_with(
identity_fakes.project_id)
+
+ def test_quota_show_no_project(self):
+ parsed_args = self.check_parser(self.cmd, [], [])
+
+ self.cmd.take_action(parsed_args)
+
+ self.quotas_mock.get.assert_called_with(identity_fakes.project_id)
+ self.volume_quotas_mock.get.assert_called_with(
+ identity_fakes.project_id)
+ self.network.get_quota.assert_called_with(identity_fakes.project_id)
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index 7f39bad0..948d9e97 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -100,8 +100,7 @@ class FakeAggregate(object):
:return:
A FakeResource object, with id and other attributes
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attribute
aggregate_info = {
@@ -177,6 +176,9 @@ class FakeComputev2Client(object):
self.hosts = mock.Mock()
self.hosts.resource_class = fakes.FakeResource(None, {})
+ self.server_groups = mock.Mock()
+ self.server_groups.resource_class = fakes.FakeResource(None, {})
+
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@@ -217,7 +219,7 @@ class FakeHypervisor(object):
"""Fake one or more hypervisor."""
@staticmethod
- def create_one_hypervisor(attrs={}):
+ def create_one_hypervisor(attrs=None):
"""Create a fake hypervisor.
:param Dictionary attrs:
@@ -225,6 +227,8 @@ class FakeHypervisor(object):
:return:
A FakeResource object, with id, hypervisor_hostname, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
hypervisor_info = {
'id': 'hypervisor-id-' + uuid.uuid4().hex,
@@ -263,7 +267,7 @@ class FakeHypervisor(object):
return hypervisor
@staticmethod
- def create_hypervisors(attrs={}, count=2):
+ def create_hypervisors(attrs=None, count=2):
"""Create multiple fake hypervisors.
:param Dictionary attrs:
@@ -284,7 +288,7 @@ class FakeHypervisorStats(object):
"""Fake one or more hypervisor stats."""
@staticmethod
- def create_one_hypervisor_stats(attrs={}):
+ def create_one_hypervisor_stats(attrs=None):
"""Create a fake hypervisor stats.
:param Dictionary attrs:
@@ -292,6 +296,8 @@ class FakeHypervisorStats(object):
:return:
A FakeResource object, with id, hypervisor_hostname, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
stats_info = {
'count': 2,
@@ -319,7 +325,7 @@ class FakeHypervisorStats(object):
return hypervisor_stats
@staticmethod
- def create_hypervisors_stats(attrs={}, count=2):
+ def create_hypervisors_stats(attrs=None, count=2):
"""Create multiple fake hypervisors stats.
:param Dictionary attrs:
@@ -349,8 +355,7 @@ class FakeSecurityGroup(object):
:return:
A FakeResource object, with id, name, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_attrs = {
@@ -400,8 +405,7 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_rule_attrs = {
@@ -445,7 +449,7 @@ class FakeServer(object):
"""Fake one or more compute servers."""
@staticmethod
- def create_one_server(attrs={}, methods={}):
+ def create_one_server(attrs=None, methods=None):
"""Create a fake server.
:param Dictionary attrs:
@@ -455,6 +459,9 @@ class FakeServer(object):
:return:
A FakeResource object, with id, name, metadata
"""
+ attrs = attrs or {}
+ methods = methods or {}
+
# Set default attributes.
server_info = {
'id': 'server-id-' + uuid.uuid4().hex,
@@ -477,7 +484,7 @@ class FakeServer(object):
return server
@staticmethod
- def create_servers(attrs={}, methods={}, count=2):
+ def create_servers(attrs=None, methods=None, count=2):
"""Create multiple fake servers.
:param Dictionary attrs:
@@ -527,8 +534,7 @@ class FakeFlavor(object):
:return:
A FakeResource object, with id, name, ram, vcpus, properties
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
flavor_info = {
@@ -566,7 +572,7 @@ class FakeFlavor(object):
return flavor
@staticmethod
- def create_flavors(attrs={}, count=2):
+ def create_flavors(attrs=None, count=2):
"""Create multiple fake flavors.
:param Dictionary attrs:
@@ -614,10 +620,9 @@ class FakeKeypair(object):
:return:
A FakeResource
"""
- # Set default attributes.
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
+ # Set default attributes.
keypair_info = {
'name': 'keypair-name-' + uuid.uuid4().hex,
'fingerprint': 'dummy',
@@ -658,7 +663,7 @@ class FakeAvailabilityZone(object):
"""Fake one or more compute availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
@@ -666,6 +671,8 @@ class FakeAvailabilityZone(object):
:return:
A FakeResource object with zoneName, zoneState, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
host_name = uuid.uuid4().hex
service_name = uuid.uuid4().hex
@@ -689,7 +696,7 @@ class FakeAvailabilityZone(object):
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
@@ -712,7 +719,7 @@ class FakeFloatingIP(object):
"""Fake one or more floating ip."""
@staticmethod
- def create_one_floating_ip(attrs={}):
+ def create_one_floating_ip(attrs=None):
"""Create a fake floating ip.
:param Dictionary attrs:
@@ -720,6 +727,8 @@ class FakeFloatingIP(object):
:return:
A FakeResource object, with id, ip, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
floating_ip_attrs = {
'id': 'floating-ip-id-' + uuid.uuid4().hex,
@@ -739,7 +748,7 @@ class FakeFloatingIP(object):
return floating_ip
@staticmethod
- def create_floating_ips(attrs={}, count=2):
+ def create_floating_ips(attrs=None, count=2):
"""Create multiple fake floating ips.
:param Dictionary attrs:
@@ -778,7 +787,7 @@ class FakeNetwork(object):
"""Fake one or more networks."""
@staticmethod
- def create_one_network(attrs={}):
+ def create_one_network(attrs=None):
"""Create a fake network.
:param Dictionary attrs:
@@ -786,6 +795,8 @@ class FakeNetwork(object):
:return:
A FakeResource object, with id, label, cidr and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
network_attrs = {
'bridge': 'br100',
@@ -831,7 +842,7 @@ class FakeNetwork(object):
return network
@staticmethod
- def create_networks(attrs={}, count=2):
+ def create_networks(attrs=None, count=2):
"""Create multiple fake networks.
:param Dictionary attrs:
@@ -860,8 +871,7 @@ class FakeHost(object):
:return:
A FakeResource object, with id and other attributes
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
host_info = {
@@ -899,3 +909,34 @@ class FakeHost(object):
info=copy.deepcopy(host_info),
loaded=True)
return host
+
+
+class FakeServerGroup(object):
+ """Fake one server group"""
+
+ @staticmethod
+ def create_one_server_group(attrs=None):
+ """Create a fake server group
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ server_group_info = {
+ 'id': 'server-group-id-' + uuid.uuid4().hex,
+ 'members': [],
+ 'metadata': {},
+ 'name': 'server-group-name-' + uuid.uuid4().hex,
+ 'policies': [],
+ 'project_id': 'server-group-project-id-' + uuid.uuid4().hex,
+ 'user_id': 'server-group-user-id-' + uuid.uuid4().hex,
+ }
+ server_group_info.update(attrs)
+ server_group = fakes.FakeResource(
+ info=copy.deepcopy(server_group_info),
+ loaded=True)
+ return server_group
diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py
index 03ca8807..fa29111b 100644
--- a/openstackclient/tests/compute/v2/test_flavor.py
+++ b/openstackclient/tests/compute/v2/test_flavor.py
@@ -288,8 +288,10 @@ class TestFlavorSet(TestFlavor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
-
- self.flavors_mock.find.assert_called_with(name='baremetal')
+ try:
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor)
+ except Exception:
+ self.flavors_mock.get.assert_called_with(parsed_args.flavor)
self.assertIsNone(result)
@@ -382,6 +384,8 @@ class TestFlavorUnset(TestFlavor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
-
- self.flavors_mock.find.assert_called_with(name='baremetal')
+ try:
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor)
+ except Exception:
+ self.flavors_mock.get.assert_called_with(parsed_args.flavor)
self.assertIsNone(result)
diff --git a/openstackclient/tests/compute/v2/test_server_group.py b/openstackclient/tests/compute/v2/test_server_group.py
new file mode 100644
index 00000000..70ff23f9
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_server_group.py
@@ -0,0 +1,283 @@
+# Copyright 2016 Huawei, Inc. All rights reserved.
+#
+# 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 mock
+
+from openstackclient.common import exceptions
+from openstackclient.common import utils
+from openstackclient.compute.v2 import server_group
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import utils as tests_utils
+
+
+class TestServerGroup(compute_fakes.TestComputev2):
+
+ fake_server_group = compute_fakes.FakeServerGroup.create_one_server_group()
+
+ columns = (
+ 'id',
+ 'members',
+ 'name',
+ 'policies',
+ 'project_id',
+ 'user_id',
+ )
+
+ data = (
+ fake_server_group.id,
+ utils.format_list(fake_server_group.members),
+ fake_server_group.name,
+ utils.format_list(fake_server_group.policies),
+ fake_server_group.project_id,
+ fake_server_group.user_id,
+ )
+
+ def setUp(self):
+ super(TestServerGroup, self).setUp()
+
+ # Get a shortcut to the ServerGroupsManager Mock
+ self.server_groups_mock = self.app.client_manager.compute.server_groups
+ self.server_groups_mock.reset_mock()
+
+
+class TestServerGroupCreate(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupCreate, self).setUp()
+
+ self.server_groups_mock.create.return_value = self.fake_server_group
+ self.cmd = server_group.CreateServerGroup(self.app, None)
+
+ def test_server_group_create(self):
+ arglist = [
+ '--policy', 'affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', ['affinity']),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.create.assert_called_once_with(
+ name=parsed_args.name,
+ policies=parsed_args.policy,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_server_group_create_with_multiple_policies(self):
+ arglist = [
+ '--policy', 'affinity',
+ '--policy', 'soft-affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', ['affinity', 'soft-affinity']),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.create.assert_called_once_with(
+ name=parsed_args.name,
+ policies=parsed_args.policy,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_server_group_create_no_policy(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+
+class TestServerGroupDelete(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupDelete, self).setUp()
+
+ self.server_groups_mock.get.return_value = self.fake_server_group
+ self.cmd = server_group.DeleteServerGroup(self.app, None)
+
+ def test_server_group_delete(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.get.assert_called_once_with('affinity_group')
+ self.server_groups_mock.delete.assert_called_once_with(
+ self.fake_server_group.id
+ )
+ self.assertIsNone(result)
+
+ def test_server_group_multiple_delete(self):
+ arglist = [
+ 'affinity_group',
+ 'anti_affinity_group'
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group', 'anti_affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.get.assert_any_call('affinity_group')
+ self.server_groups_mock.get.assert_any_call('anti_affinity_group')
+ self.server_groups_mock.delete.assert_called_with(
+ self.fake_server_group.id
+ )
+ self.assertEqual(2, self.server_groups_mock.get.call_count)
+ self.assertEqual(2, self.server_groups_mock.delete.call_count)
+ self.assertIsNone(result)
+
+ def test_server_group_delete_no_input(self):
+ arglist = []
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+ def test_server_group_multiple_delete_with_exception(self):
+ arglist = [
+ 'affinity_group',
+ 'anti_affinity_group'
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group', 'anti_affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ find_mock_result = [self.fake_server_group, exceptions.CommandError]
+ with mock.patch.object(utils, 'find_resource',
+ side_effect=find_mock_result) as find_mock:
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 server groups failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.server_groups_mock,
+ 'affinity_group')
+ find_mock.assert_any_call(self.server_groups_mock,
+ 'anti_affinity_group')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.server_groups_mock.delete.assert_called_once_with(
+ self.fake_server_group.id
+ )
+
+
+class TestServerGroupList(TestServerGroup):
+
+ list_columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ )
+
+ list_columns_long = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+
+ list_data = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ utils.format_list(TestServerGroup.fake_server_group.policies),
+ ),)
+
+ list_data_long = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ utils.format_list(TestServerGroup.fake_server_group.policies),
+ utils.format_list(TestServerGroup.fake_server_group.members),
+ TestServerGroup.fake_server_group.project_id,
+ TestServerGroup.fake_server_group.user_id,
+ ),)
+
+ def setUp(self):
+ super(TestServerGroupList, self).setUp()
+
+ self.server_groups_mock.list.return_value = [self.fake_server_group]
+ self.cmd = server_group.ListServerGroup(self.app, None)
+
+ def test_server_group_list(self):
+ arglist = []
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.list.assert_called_once_with(False)
+
+ self.assertEqual(self.list_columns, columns)
+ self.assertEqual(self.list_data, tuple(data))
+
+ def test_server_group_list_with_all_projects_and_long(self):
+ arglist = [
+ '--all-projects',
+ '--long',
+ ]
+ verifylist = [
+ ('all_projects', True),
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.list.assert_called_once_with(True)
+
+ self.assertEqual(self.list_columns_long, columns)
+ self.assertEqual(self.list_data_long, tuple(data))
+
+
+class TestServerGroupShow(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupShow, self).setUp()
+
+ self.server_groups_mock.get.return_value = self.fake_server_group
+ self.cmd = server_group.ShowServerGroup(self.app, None)
+
+ def test_server_group_show(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('server_group', 'affinity_group'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index f0cebb06..46f983dc 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -142,7 +142,7 @@ class FakeModule(object):
class FakeResource(object):
- def __init__(self, manager=None, info={}, loaded=False, methods={}):
+ def __init__(self, manager=None, info=None, loaded=False, methods=None):
"""Set attributes and methods for a resource.
:param manager:
@@ -154,6 +154,9 @@ class FakeResource(object):
:param Dictionary methods:
A dictionary with all methods
"""
+ info = info or {}
+ methods = methods or {}
+
self.__name__ = type(self).__name__
self.manager = manager
self._info = info
@@ -189,9 +192,12 @@ class FakeResource(object):
class FakeResponse(requests.Response):
- def __init__(self, headers={}, status_code=200, data=None, encoding=None):
+ def __init__(self, headers=None, status_code=200,
+ data=None, encoding=None):
super(FakeResponse, self).__init__()
+ headers = headers or {}
+
self.status_code = status_code
self.headers.update(headers)
diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py
index 3555d2d4..f90d846d 100644
--- a/openstackclient/tests/image/v2/fakes.py
+++ b/openstackclient/tests/image/v2/fakes.py
@@ -181,7 +181,7 @@ class FakeImage(object):
"""
@staticmethod
- def create_one_image(attrs={}):
+ def create_one_image(attrs=None):
"""Create a fake image.
:param Dictionary attrs:
@@ -190,6 +190,8 @@ class FakeImage(object):
A FakeResource object with id, name, owner, protected,
visibility and tags attrs
"""
+ attrs = attrs or {}
+
# Set default attribute
image_info = {
'id': 'image-id' + uuid.uuid4().hex,
@@ -210,7 +212,7 @@ class FakeImage(object):
return image
@staticmethod
- def create_images(attrs={}, count=2):
+ def create_images(attrs=None, count=2):
"""Create multiple fake images.
:param Dictionary attrs:
diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py
index 73c6b09e..1989b515 100644
--- a/openstackclient/tests/network/v2/fakes.py
+++ b/openstackclient/tests/network/v2/fakes.py
@@ -83,8 +83,7 @@ class FakeAddressScope(object):
:return:
A FakeResource object with name, id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
address_scope_attrs = {
@@ -112,7 +111,7 @@ class FakeAvailabilityZone(object):
"""Fake one or more network availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
@@ -120,6 +119,8 @@ class FakeAvailabilityZone(object):
:return:
A FakeResource object with name, state, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
availability_zone = {
'name': uuid.uuid4().hex,
@@ -136,7 +137,7 @@ class FakeAvailabilityZone(object):
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
@@ -159,7 +160,7 @@ class FakeNetwork(object):
"""Fake one or more networks."""
@staticmethod
- def create_one_network(attrs={}):
+ def create_one_network(attrs=None):
"""Create a fake network.
:param Dictionary attrs:
@@ -168,6 +169,8 @@ class FakeNetwork(object):
A FakeResource object, with id, name, admin_state_up,
router_external, status, subnets, tenant_id
"""
+ attrs = attrs or {}
+
# Set default attributes.
network_attrs = {
'id': 'network-id-' + uuid.uuid4().hex,
@@ -196,7 +199,7 @@ class FakeNetwork(object):
return network
@staticmethod
- def create_networks(attrs={}, count=2):
+ def create_networks(attrs=None, count=2):
"""Create multiple fake networks.
:param Dictionary attrs:
@@ -236,16 +239,16 @@ class FakePort(object):
"""Fake one or more ports."""
@staticmethod
- def create_one_port(attrs={}):
+ def create_one_port(attrs=None):
"""Create a fake port.
:param Dictionary attrs:
A dictionary with all attributes
- :param Dictionary methods:
- A dictionary with all methods
:return:
A FakeResource object, with id, name, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
port_attrs = {
'admin_state_up': True,
@@ -288,7 +291,7 @@ class FakePort(object):
return port
@staticmethod
- def create_ports(attrs={}, count=2):
+ def create_ports(attrs=None, count=2):
"""Create multiple fake ports.
:param Dictionary attrs:
@@ -414,8 +417,7 @@ class FakeSecurityGroup(object):
:return:
A FakeResource object, with id, name, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_attrs = {
@@ -469,8 +471,7 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_rule_attrs = {
@@ -521,7 +522,7 @@ class FakeSubnet(object):
"""Fake one or more subnets."""
@staticmethod
- def create_one_subnet(attrs={}):
+ def create_one_subnet(attrs=None):
"""Create a fake subnet.
:param Dictionary attrs:
@@ -529,6 +530,8 @@ class FakeSubnet(object):
:return:
A FakeResource object faking the subnet
"""
+ attrs = attrs or {}
+
# Set default attributes.
project_id = 'project-id-' + uuid.uuid4().hex
subnet_attrs = {
@@ -553,13 +556,14 @@ class FakeSubnet(object):
subnet = fakes.FakeResource(info=copy.deepcopy(subnet_attrs),
loaded=True)
+
# Set attributes with special mappings in OpenStack SDK.
subnet.project_id = subnet_attrs['tenant_id']
return subnet
@staticmethod
- def create_subnets(attrs={}, count=2):
+ def create_subnets(attrs=None, count=2):
"""Create multiple fake subnets.
:param Dictionary attrs:
@@ -580,7 +584,7 @@ class FakeFloatingIP(object):
"""Fake one or more floating ip."""
@staticmethod
- def create_one_floating_ip(attrs={}):
+ def create_one_floating_ip(attrs=None):
"""Create a fake floating ip.
:param Dictionary attrs:
@@ -588,6 +592,8 @@ class FakeFloatingIP(object):
:return:
A FakeResource object, with id, ip, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
floating_ip_attrs = {
'id': 'floating-ip-id-' + uuid.uuid4().hex,
@@ -616,7 +622,7 @@ class FakeFloatingIP(object):
return floating_ip
@staticmethod
- def create_floating_ips(attrs={}, count=2):
+ def create_floating_ips(attrs=None, count=2):
"""Create multiple fake floating ips.
:param Dictionary attrs:
@@ -655,7 +661,7 @@ class FakeSubnetPool(object):
"""Fake one or more subnet pools."""
@staticmethod
- def create_one_subnet_pool(attrs={}):
+ def create_one_subnet_pool(attrs=None):
"""Create a fake subnet pool.
:param Dictionary attrs:
@@ -663,6 +669,8 @@ class FakeSubnetPool(object):
:return:
A FakeResource object faking the subnet pool
"""
+ attrs = attrs or {}
+
# Set default attributes.
subnet_pool_attrs = {
'id': 'subnet-pool-id-' + uuid.uuid4().hex,
@@ -693,7 +701,7 @@ class FakeSubnetPool(object):
return subnet_pool
@staticmethod
- def create_subnet_pools(attrs={}, count=2):
+ def create_subnet_pools(attrs=None, count=2):
"""Create multiple fake subnet pools.
:param Dictionary attrs:
diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py
index 7d0f8717..a1b0aec9 100644
--- a/openstackclient/tests/network/v2/test_network.py
+++ b/openstackclient/tests/network/v2/test_network.py
@@ -484,6 +484,9 @@ class TestSetNetwork(TestNetwork):
'--share',
'--external',
'--default',
+ '--provider-network-type', 'vlan',
+ '--provider-physical-network', 'physnet1',
+ '--provider-segment', '400',
]
verifylist = [
('network', self._network.name),
@@ -492,6 +495,9 @@ class TestSetNetwork(TestNetwork):
('share', True),
('external', True),
('default', True),
+ ('provider_network_type', 'vlan'),
+ ('physical_network', 'physnet1'),
+ ('segmentation_id', '400'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -503,6 +509,9 @@ class TestSetNetwork(TestNetwork):
'shared': True,
'router:external': True,
'is_default': True,
+ 'provider:network_type': 'vlan',
+ 'provider:physical_network': 'physnet1',
+ 'provider:segmentation_id': '400',
}
self.network.update_network.assert_called_once_with(
self._network, **attrs)
diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py
index c2fa1256..df7414aa 100644
--- a/openstackclient/tests/network/v2/test_security_group_rule.py
+++ b/openstackclient/tests/network/v2/test_security_group_rule.py
@@ -532,31 +532,46 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
_security_group_rules = [_security_group_rule_tcp,
_security_group_rule_icmp]
- expected_columns_with_group = (
+ expected_columns_with_group_and_long = (
'ID',
'IP Protocol',
'IP Range',
'Port Range',
+ 'Direction',
+ 'Ethertype',
'Remote Security Group',
)
- expected_columns_no_group = \
- expected_columns_with_group + ('Security Group',)
+ expected_columns_no_group = (
+ 'ID',
+ 'IP Protocol',
+ 'IP Range',
+ 'Port Range',
+ 'Remote Security Group',
+ 'Security Group',
+ )
- expected_data_with_group = []
+ expected_data_with_group_and_long = []
expected_data_no_group = []
for _security_group_rule in _security_group_rules:
- expected_rule_with_group = (
+ expected_data_with_group_and_long.append((
_security_group_rule.id,
_security_group_rule.protocol,
_security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range(
_security_group_rule),
+ _security_group_rule.direction,
+ _security_group_rule.ethertype,
_security_group_rule.remote_group_id,
- )
- expected_rule_no_group = expected_rule_with_group + \
- (_security_group_rule.security_group_id,)
- expected_data_with_group.append(expected_rule_with_group)
- expected_data_no_group.append(expected_rule_no_group)
+ ))
+ expected_data_no_group.append((
+ _security_group_rule.id,
+ _security_group_rule.protocol,
+ _security_group_rule.remote_ip_prefix,
+ security_group_rule._format_network_port_range(
+ _security_group_rule),
+ _security_group_rule.remote_group_id,
+ _security_group_rule.security_group_id,
+ ))
def setUp(self):
super(TestListSecurityGroupRuleNetwork, self).setUp()
@@ -570,7 +585,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.cmd = security_group_rule.ListSecurityGroupRule(
self.app, self.namespace)
- def test_list_no_group(self):
+ def test_list_default(self):
self._security_group_rule_tcp.port_range_min = 80
parsed_args = self.check_parser(self.cmd, [], [])
@@ -580,12 +595,14 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.assertEqual(self.expected_columns_no_group, columns)
self.assertEqual(self.expected_data_no_group, list(data))
- def test_list_with_group(self):
+ def test_list_with_group_and_long(self):
self._security_group_rule_tcp.port_range_min = 80
arglist = [
+ '--long',
self._security_group.id,
]
verifylist = [
+ ('long', True),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -595,8 +612,24 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.network.security_group_rules.assert_called_once_with(**{
'security_group_id': self._security_group.id,
})
- self.assertEqual(self.expected_columns_with_group, columns)
- self.assertEqual(self.expected_data_with_group, list(data))
+ self.assertEqual(self.expected_columns_with_group_and_long, columns)
+ self.assertEqual(self.expected_data_with_group_and_long, list(data))
+
+ def test_list_with_ignored_options(self):
+ self._security_group_rule_tcp.port_range_min = 80
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('all_projects', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.security_group_rules.assert_called_once_with(**{})
+ self.assertEqual(self.expected_columns_no_group, columns)
+ self.assertEqual(self.expected_data_no_group, list(data))
class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
@@ -665,11 +698,13 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
# Get the command object to test
self.cmd = security_group_rule.ListSecurityGroupRule(self.app, None)
- def test_list_no_group(self):
+ def test_list_default(self):
parsed_args = self.check_parser(self.cmd, [], [])
columns, data = self.cmd.take_action(parsed_args)
- self.compute.security_groups.list.assert_called_once_with()
+ self.compute.security_groups.list.assert_called_once_with(
+ search_opts={'all_tenants': False}
+ )
self.assertEqual(self.expected_columns_no_group, columns)
self.assertEqual(self.expected_data_no_group, list(data))
@@ -689,6 +724,38 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
self.assertEqual(self.expected_columns_with_group, columns)
self.assertEqual(self.expected_data_with_group, list(data))
+ def test_list_all_projects(self):
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('all_projects', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.compute.security_groups.list.assert_called_once_with(
+ search_opts={'all_tenants': True}
+ )
+ self.assertEqual(self.expected_columns_no_group, columns)
+ self.assertEqual(self.expected_data_no_group, list(data))
+
+ def test_list_with_ignored_options(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.compute.security_groups.list.assert_called_once_with(
+ search_opts={'all_tenants': False}
+ )
+ self.assertEqual(self.expected_columns_no_group, columns)
+ self.assertEqual(self.expected_data_no_group, list(data))
+
class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py
index cbb32fc3..7797e4d0 100644
--- a/openstackclient/tests/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/network/v2/test_subnet_pool.py
@@ -113,6 +113,17 @@ class TestCreateSubnetPool(TestSubnetPool):
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
+ def test_create_no_pool_prefix(self):
+ """Make sure --pool-prefix is a required argument"""
+ arglist = [
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('name', self._subnet_pool.name),
+ ]
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
def test_create_default_options(self):
arglist = [
'--pool-prefix', '10.0.10.0/24',
@@ -138,23 +149,26 @@ class TestCreateSubnetPool(TestSubnetPool):
'--default-prefix-length', self._subnet_pool.default_prefixlen,
'--max-prefix-length', self._subnet_pool.max_prefixlen,
'--min-prefix-length', self._subnet_pool.min_prefixlen,
+ '--pool-prefix', '10.0.10.0/24',
self._subnet_pool.name,
]
verifylist = [
- ('default_prefix_length', self._subnet_pool.default_prefixlen),
- ('max_prefix_length', self._subnet_pool.max_prefixlen),
- ('min_prefix_length', self._subnet_pool.min_prefixlen),
+ ('default_prefix_length',
+ int(self._subnet_pool.default_prefixlen)),
+ ('max_prefix_length', int(self._subnet_pool.max_prefixlen)),
+ ('min_prefix_length', int(self._subnet_pool.min_prefixlen)),
('name', self._subnet_pool.name),
+ ('prefixes', ['10.0.10.0/24']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
self.network.create_subnet_pool.assert_called_once_with(**{
- 'default_prefixlen': self._subnet_pool.default_prefixlen,
- 'max_prefixlen': self._subnet_pool.max_prefixlen,
- 'min_prefixlen': self._subnet_pool.min_prefixlen,
- 'prefixes': [],
+ 'default_prefixlen': int(self._subnet_pool.default_prefixlen),
+ 'max_prefixlen': int(self._subnet_pool.max_prefixlen),
+ 'min_prefixlen': int(self._subnet_pool.min_prefixlen),
+ 'prefixes': ['10.0.10.0/24'],
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
@@ -221,6 +235,32 @@ class TestCreateSubnetPool(TestSubnetPool):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_create_default_and_shared_options(self):
+ arglist = [
+ '--pool-prefix', '10.0.10.0/24',
+ '--default',
+ '--share',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('prefixes', ['10.0.10.0/24']),
+ ('default', True),
+ ('share', True),
+ ('name', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet_pool.assert_called_once_with(**{
+ 'is_default': True,
+ 'name': self._subnet_pool.name,
+ 'prefixes': ['10.0.10.0/24'],
+ 'shared': True,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestDeleteSubnetPool(TestSubnetPool):
@@ -267,6 +307,8 @@ class TestListSubnetPool(TestSubnetPool):
columns_long = columns + (
'Default Prefix Length',
'Address Scope',
+ 'Default Subnet Pool',
+ 'Shared',
)
data = []
@@ -285,6 +327,8 @@ class TestListSubnetPool(TestSubnetPool):
utils.format_list(pool.prefixes),
pool.default_prefixlen,
pool.address_scope_id,
+ pool.is_default,
+ pool.shared,
))
def setUp(self):
@@ -354,8 +398,8 @@ class TestSetSubnetPool(TestSubnetPool):
]
verifylist = [
('name', 'noob'),
- ('default_prefix_length', '8'),
- ('min_prefix_length', '8'),
+ ('default_prefix_length', 8),
+ ('min_prefix_length', 8),
('subnet_pool', self._subnet_pool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -364,8 +408,8 @@ class TestSetSubnetPool(TestSubnetPool):
attrs = {
'name': 'noob',
- 'default_prefixlen': '8',
- 'min_prefixlen': '8',
+ 'default_prefixlen': 8,
+ 'min_prefixlen': 8,
}
self.network.update_subnet_pool.assert_called_once_with(
self._subnet_pool, **attrs)
@@ -380,7 +424,7 @@ class TestSetSubnetPool(TestSubnetPool):
]
verifylist = [
('prefixes', ['10.0.1.0/24', '10.0.2.0/24']),
- ('max_prefix_length', '16'),
+ ('max_prefix_length', 16),
('subnet_pool', self._subnet_pool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -391,7 +435,7 @@ class TestSetSubnetPool(TestSubnetPool):
prefixes.extend(self._subnet_pool.prefixes)
attrs = {
'prefixes': prefixes,
- 'max_prefixlen': '16',
+ 'max_prefixlen': 16,
}
self.network.update_subnet_pool.assert_called_once_with(
self._subnet_pool, **attrs)
@@ -474,6 +518,62 @@ class TestSetSubnetPool(TestSubnetPool):
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
+ def test_set_default(self):
+ arglist = [
+ '--default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'is_default': True
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_default(self):
+ arglist = [
+ '--no-default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('no_default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'is_default': False,
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_default_conflict(self):
+ arglist = [
+ '--default',
+ '--no-default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('default', True),
+ ('no_default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+
+ # Exclusive arguments will conflict here.
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
class TestShowSubnetPool(TestSubnetPool):
diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py
index ab97dd91..90454fc2 100644
--- a/openstackclient/tests/test_shell.py
+++ b/openstackclient/tests/test_shell.py
@@ -112,7 +112,7 @@ global_options = {
'--os-default-domain': (DEFAULT_DOMAIN_NAME, True, True),
'--os-cacert': ('/dev/null', True, True),
'--timing': (True, True, False),
- '--profile': ('SECRET_KEY', True, False),
+ '--os-profile': ('SECRET_KEY', True, False),
'--os-interface': (DEFAULT_INTERFACE, True, True)
}
diff --git a/openstackclient/tests/volume/v1/fakes.py b/openstackclient/tests/volume/v1/fakes.py
index 42673efa..d6c46439 100644
--- a/openstackclient/tests/volume/v1/fakes.py
+++ b/openstackclient/tests/volume/v1/fakes.py
@@ -129,6 +129,97 @@ QOS_WITH_ASSOCIATIONS = {
}
+class FakeServiceClient(object):
+
+ def __init__(self, **kwargs):
+ self.services = mock.Mock()
+ self.services.resource_class = fakes.FakeResource(None, {})
+
+
+class TestService(utils.TestCommand):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ self.app.client_manager.volume = FakeServiceClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN
+ )
+
+
+class FakeService(object):
+ """Fake one or more Services."""
+
+ @staticmethod
+ def create_one_service(attrs=None):
+ """Create a fake service.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :retrun:
+ A FakeResource object with host, status, etc.
+ """
+ # Set default attribute
+ service_info = {
+ 'host': 'host_test',
+ 'binary': 'cinder_test',
+ 'status': 'enabled',
+ 'disabled_reason': 'LongHoliday-GoldenWeek',
+ 'zone': 'fake_zone',
+ 'updated_at': 'fake_date',
+ 'state': 'fake_state',
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ if attrs is None:
+ attrs = {}
+ service_info.update(attrs)
+
+ service = fakes.FakeResource(
+ None,
+ service_info,
+ loaded=True)
+
+ return service
+
+ @staticmethod
+ def create_services(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :param Integer count:
+ The number of services to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ services = []
+ for n in range(0, count):
+ services.append(FakeService.create_one_service(attrs))
+
+ return services
+
+ @staticmethod
+ def get_services(services=None, count=2):
+ """Get an iterable MagicMock object with a list of faked services.
+
+ If services list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List services:
+ A list of FakeResource objects faking services
+ :param Integer count:
+ The number of services to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ services
+ """
+ if services is None:
+ services = FakeService.create_services(count)
+
+ return mock.MagicMock(side_effect=services)
+
+
class FakeImagev1Client(object):
def __init__(self, **kwargs):
diff --git a/openstackclient/tests/volume/v1/test_service.py b/openstackclient/tests/volume/v1/test_service.py
new file mode 100644
index 00000000..71684344
--- /dev/null
+++ b/openstackclient/tests/volume/v1/test_service.py
@@ -0,0 +1,141 @@
+#
+# 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.
+#
+
+
+from openstackclient.tests.volume.v1 import fakes as service_fakes
+from openstackclient.volume.v1 import service
+
+
+class TestService(service_fakes.TestService):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.service_mock = self.app.client_manager.volume.services
+ self.service_mock.reset_mock()
+
+
+class TestServiceList(TestService):
+
+ # The service to be listed
+ services = service_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestServiceList, self).setUp()
+
+ self.service_mock.list.return_value = [self.services]
+
+ # Get the command object to test
+ self.cmd = service.ListService(self.app, None)
+
+ def test_service_list(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to list services
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
+
+ # checking if prohibited columns are present in output
+ self.assertNotIn("Disabled Reason", columns)
+ self.assertNotIn(self.services.disabled_reason,
+ tuple(data))
+
+ def test_service_list_with_long_option(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ '--long'
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ('long', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ 'Disabled Reason'
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ self.services.disabled_reason,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index 3c238d10..120666a0 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -232,6 +232,97 @@ EXTENSION = {
}
+class FakeServiceClient(object):
+
+ def __init__(self, **kwargs):
+ self.services = mock.Mock()
+ self.services.resource_class = fakes.FakeResource(None, {})
+
+
+class TestService(utils.TestCommand):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ self.app.client_manager.volume = FakeServiceClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN
+ )
+
+
+class FakeService(object):
+ """Fake one or more Services."""
+
+ @staticmethod
+ def create_one_service(attrs=None):
+ """Create a fake service.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :retrun:
+ A FakeResource object with host, status, etc.
+ """
+ # Set default attribute
+ service_info = {
+ 'host': 'host_test',
+ 'binary': 'cinder_test',
+ 'status': 'enabled',
+ 'disabled_reason': 'LongHoliday-GoldenWeek',
+ 'zone': 'fake_zone',
+ 'updated_at': 'fake_date',
+ 'state': 'fake_state',
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ if attrs is None:
+ attrs = {}
+ service_info.update(attrs)
+
+ service = fakes.FakeResource(
+ None,
+ service_info,
+ loaded=True)
+
+ return service
+
+ @staticmethod
+ def create_services(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :param Integer count:
+ The number of services to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ services = []
+ for n in range(0, count):
+ services.append(FakeService.create_one_service(attrs))
+
+ return services
+
+ @staticmethod
+ def get_services(services=None, count=2):
+ """Get an iterable MagicMock object with a list of faked services.
+
+ If services list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List services:
+ A list of FakeResource objects faking services
+ :param Integer count:
+ The number of services to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ services
+ """
+ if services is None:
+ services = FakeService.create_services(count)
+
+ return mock.MagicMock(side_effect=services)
+
+
class FakeVolumeClient(object):
def __init__(self, **kwargs):
@@ -281,7 +372,7 @@ class FakeVolume(object):
"""
@staticmethod
- def create_one_volume(attrs={}):
+ def create_one_volume(attrs=None):
"""Create a fake volume.
:param Dictionary attrs:
@@ -289,6 +380,8 @@ class FakeVolume(object):
:retrun:
A FakeResource object with id, name, status, etc.
"""
+ attrs = attrs or {}
+
# Set default attribute
volume_info = {
'id': 'volume-id' + uuid.uuid4().hex,
@@ -320,7 +413,7 @@ class FakeVolume(object):
return volume
@staticmethod
- def create_volumes(attrs={}, count=2):
+ def create_volumes(attrs=None, count=2):
"""Create multiple fake volumes.
:param Dictionary attrs:
@@ -361,16 +454,16 @@ class FakeAvailabilityZone(object):
"""Fake one or more volume availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}, methods={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
A dictionary with all attributes
- :param Dictionary methods:
- A dictionary with all methods
:return:
A FakeResource object with zoneName, zoneState, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
availability_zone = {
'zoneName': uuid.uuid4().hex,
@@ -382,18 +475,15 @@ class FakeAvailabilityZone(object):
availability_zone = fakes.FakeResource(
info=copy.deepcopy(availability_zone),
- methods=methods,
loaded=True)
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, methods={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
A dictionary with all attributes
- :param Dictionary methods:
- A dictionary with all methods
:param int count:
The number of AZs to fake
:return:
@@ -402,8 +492,7 @@ class FakeAvailabilityZone(object):
availability_zones = []
for i in range(0, count):
availability_zone = \
- FakeAvailabilityZone.create_one_availability_zone(
- attrs, methods)
+ FakeAvailabilityZone.create_one_availability_zone(attrs)
availability_zones.append(availability_zone)
return availability_zones
diff --git a/openstackclient/tests/volume/v2/test_service.py b/openstackclient/tests/volume/v2/test_service.py
new file mode 100644
index 00000000..ba2e1b32
--- /dev/null
+++ b/openstackclient/tests/volume/v2/test_service.py
@@ -0,0 +1,141 @@
+#
+# 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.
+#
+
+
+from openstackclient.tests.volume.v2 import fakes as service_fakes
+from openstackclient.volume.v2 import service
+
+
+class TestService(service_fakes.TestService):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.service_mock = self.app.client_manager.volume.services
+ self.service_mock.reset_mock()
+
+
+class TestServiceList(TestService):
+
+ # The service to be listed
+ services = service_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestServiceList, self).setUp()
+
+ self.service_mock.list.return_value = [self.services]
+
+ # Get the command object to test
+ self.cmd = service.ListService(self.app, None)
+
+ def test_service_list(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to list services
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
+
+ # checking if prohibited columns are present in output
+ self.assertNotIn("Disabled Reason", columns)
+ self.assertNotIn(self.services.disabled_reason,
+ tuple(data))
+
+ def test_service_list_with_long_option(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ '--long'
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ('long', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ 'Disabled Reason'
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ self.services.disabled_reason,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py
new file mode 100644
index 00000000..f26be13e
--- /dev/null
+++ b/openstackclient/volume/v1/service.py
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+
+"""Service action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import utils
+
+
+class ListService(command.Lister):
+ """List service command"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListService, self).get_parser(prog_name)
+ parser.add_argument(
+ "--host",
+ metavar="<host>",
+ help="List services on specified host (name only)")
+ parser.add_argument(
+ "--service",
+ metavar="<service>",
+ help="List only specified service (name only)")
+ parser.add_argument(
+ "--long",
+ action="store_true",
+ default=False,
+ help="List additional fields in output"
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+
+ if parsed_args.long:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ "Disabled Reason"
+ ]
+ else:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At"
+ ]
+
+ data = service_client.services.list(parsed_args.host,
+ parsed_args.service)
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py
new file mode 100644
index 00000000..f26be13e
--- /dev/null
+++ b/openstackclient/volume/v2/service.py
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+
+"""Service action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import utils
+
+
+class ListService(command.Lister):
+ """List service command"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListService, self).get_parser(prog_name)
+ parser.add_argument(
+ "--host",
+ metavar="<host>",
+ help="List services on specified host (name only)")
+ parser.add_argument(
+ "--service",
+ metavar="<service>",
+ help="List only specified service (name only)")
+ parser.add_argument(
+ "--long",
+ action="store_true",
+ default=False,
+ help="List additional fields in output"
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+
+ if parsed_args.long:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ "Disabled Reason"
+ ]
+ else:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At"
+ ]
+
+ data = service_client.services.list(parsed_args.host,
+ parsed_args.service)
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))