summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/object_store_v1.py14
-rw-r--r--openstackclient/compute/v2/server.py9
-rw-r--r--openstackclient/identity/common.py24
-rw-r--r--openstackclient/identity/v3/domain.py9
-rw-r--r--openstackclient/identity/v3/project.py17
-rw-r--r--openstackclient/identity/v3/role.py10
-rw-r--r--openstackclient/network/sdk_utils.py5
-rw-r--r--openstackclient/network/v2/address_scope.py10
-rw-r--r--openstackclient/network/v2/floating_ip.py2
-rw-r--r--openstackclient/network/v2/floating_ip_port_forwarding.py20
-rw-r--r--openstackclient/network/v2/ip_availability.py2
-rw-r--r--openstackclient/network/v2/network.py8
-rw-r--r--openstackclient/network/v2/network_agent.py1
-rw-r--r--openstackclient/network/v2/network_auto_allocated_topology.py17
-rw-r--r--openstackclient/network/v2/network_flavor.py10
-rw-r--r--openstackclient/network/v2/network_flavor_profile.py10
-rw-r--r--openstackclient/network/v2/network_meter.py10
-rw-r--r--openstackclient/network/v2/network_meter_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_policy.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py8
-rw-r--r--openstackclient/network/v2/network_rbac.py32
-rw-r--r--openstackclient/network/v2/network_segment.py10
-rw-r--r--openstackclient/network/v2/network_segment_range.py9
-rw-r--r--openstackclient/network/v2/port.py1
-rw-r--r--openstackclient/network/v2/router.py98
-rw-r--r--openstackclient/network/v2/security_group.py37
-rw-r--r--openstackclient/network/v2/security_group_rule.py10
-rw-r--r--openstackclient/network/v2/subnet.py1
-rw-r--r--openstackclient/network/v2/subnet_pool.py1
-rw-r--r--openstackclient/tests/functional/identity/v3/test_project.py1
-rw-r--r--openstackclient/tests/functional/network/v2/test_router.py43
-rw-r--r--openstackclient/tests/functional/network/v2/test_security_group.py4
-rw-r--r--openstackclient/tests/unit/api/test_object_store_v1.py60
-rw-r--r--openstackclient/tests/unit/common/test_parseractions.py4
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py36
-rw-r--r--openstackclient/tests/unit/identity/v3/test_access_rule.py2
-rw-r--r--openstackclient/tests/unit/identity/v3/test_domain.py110
-rw-r--r--openstackclient/tests/unit/identity/v3/test_project.py281
-rw-r--r--openstackclient/tests/unit/identity/v3/test_role.py162
-rw-r--r--openstackclient/tests/unit/integ/cli/test_shell.py20
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py4
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py15
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py33
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py74
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py140
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_network.py10
47 files changed, 1127 insertions, 287 deletions
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index c8514a57..8092abd0 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -87,7 +87,7 @@ class APIv1(api.BaseAPI):
def container_list(
self,
- all_data=False,
+ full_listing=False,
limit=None,
marker=None,
end_marker=None,
@@ -96,7 +96,7 @@ class APIv1(api.BaseAPI):
):
"""Get containers in an account
- :param boolean all_data:
+ :param boolean full_listing:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
@@ -113,7 +113,7 @@ class APIv1(api.BaseAPI):
params['format'] = 'json'
- if all_data:
+ if full_listing:
data = listing = self.container_list(
limit=limit,
marker=marker,
@@ -299,7 +299,7 @@ class APIv1(api.BaseAPI):
def object_list(
self,
container=None,
- all_data=False,
+ full_listing=False,
limit=None,
marker=None,
end_marker=None,
@@ -311,7 +311,7 @@ class APIv1(api.BaseAPI):
:param string container:
container name to get a listing for
- :param boolean all_data:
+ :param boolean full_listing:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
@@ -328,11 +328,11 @@ class APIv1(api.BaseAPI):
headers will be a dict and all header names will be lowercase.
"""
- if container is None or object is None:
+ if container is None:
return None
params['format'] = 'json'
- if all_data:
+ if full_listing:
data = listing = self.object_list(
container=container,
limit=limit,
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 8be78049..93e9f966 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -1921,9 +1921,12 @@ class RebuildServer(command.ShowOne):
compute_client.servers, parsed_args.server)
# If parsed_args.image is not set, default to the currently used one.
- image_id = parsed_args.image or server.to_dict().get(
- 'image', {}).get('id')
- image = image_client.get_image(image_id)
+ if parsed_args.image:
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False)
+ else:
+ image_id = server.to_dict().get('image', {}).get('id')
+ image = image_client.get_image(image_id)
kwargs = {}
if parsed_args.property:
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index 7be2a17b..e70d87d2 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -213,6 +213,15 @@ def _find_identity_resource(identity_client_manager, name_or_id,
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
+def get_immutable_options(parsed_args):
+ options = {}
+ if parsed_args.immutable:
+ options['immutable'] = True
+ if parsed_args.no_immutable:
+ options['immutable'] = False
+ return options
+
+
def add_user_domain_option_to_parser(parser):
parser.add_argument(
'--user-domain',
@@ -261,3 +270,18 @@ def add_inherited_option_to_parser(parser):
help=_('Specifies if the role grant is inheritable to the sub '
'projects'),
)
+
+
+def add_resource_option_to_parser(parser):
+ enable_group = parser.add_mutually_exclusive_group()
+ enable_group.add_argument(
+ '--immutable',
+ action='store_true',
+ help=_('Make resource immutable. An immutable project may not '
+ 'be deleted or modified except to remove the immutable flag'),
+ )
+ enable_group.add_argument(
+ '--no-immutable',
+ action='store_true',
+ help=_('Make resource mutable (default)'),
+ )
diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py
index dbcc97f6..e33fce05 100644
--- a/openstackclient/identity/v3/domain.py
+++ b/openstackclient/identity/v3/domain.py
@@ -60,6 +60,7 @@ class CreateDomain(command.ShowOne):
action='store_true',
help=_('Return existing domain'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -69,10 +70,13 @@ class CreateDomain(command.ShowOne):
if parsed_args.disable:
enabled = False
+ options = common.get_immutable_options(parsed_args)
+
try:
domain = identity_client.domains.create(
name=parsed_args.name,
description=parsed_args.description,
+ options=options,
enabled=enabled,
)
except ks_exc.Conflict:
@@ -163,6 +167,7 @@ class SetDomain(command.Command):
action='store_true',
help=_('Disable domain'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -180,6 +185,10 @@ class SetDomain(command.Command):
if parsed_args.disable:
kwargs['enabled'] = False
+ options = common.get_immutable_options(parsed_args)
+ if options:
+ kwargs['options'] = options
+
identity_client.domains.update(domain.id, **kwargs)
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 9ecc70ef..5e8ce829 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -78,6 +78,7 @@ class CreateProject(command.ShowOne):
action='store_true',
help=_('Return existing project'),
)
+ common.add_resource_option_to_parser(parser)
tag.add_tag_option_to_parser_for_create(parser, _('project'))
return parser
@@ -99,9 +100,20 @@ class CreateProject(command.ShowOne):
enabled = True
if parsed_args.disable:
enabled = False
+
+ options = common.get_immutable_options(parsed_args)
+
kwargs = {}
if parsed_args.property:
kwargs = parsed_args.property.copy()
+ if 'is_domain' in kwargs.keys():
+ if kwargs['is_domain'].lower() == "true":
+ kwargs['is_domain'] = True
+ elif kwargs['is_domain'].lower() == "false":
+ kwargs['is_domain'] = False
+ elif kwargs['is_domain'].lower() == "none":
+ kwargs['is_domain'] = None
+
kwargs['tags'] = list(set(parsed_args.tags))
try:
@@ -111,6 +123,7 @@ class CreateProject(command.ShowOne):
parent=parent,
description=parsed_args.description,
enabled=enabled,
+ options=options,
**kwargs
)
except ks_exc.Conflict:
@@ -317,6 +330,7 @@ class SetProject(command.Command):
help=_('Set a property on <project> '
'(repeat option to set multiple properties)'),
)
+ common.add_resource_option_to_parser(parser)
tag.add_tag_option_to_parser_for_set(parser, _('project'))
return parser
@@ -336,6 +350,9 @@ class SetProject(command.Command):
kwargs['enabled'] = True
if parsed_args.disable:
kwargs['enabled'] = False
+ options = common.get_immutable_options(parsed_args)
+ if options:
+ kwargs['options'] = options
if parsed_args.property:
kwargs.update(parsed_args.property)
tag.update_tags_in_args(parsed_args, project, kwargs)
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 36f3f938..980ebf11 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -191,6 +191,7 @@ class CreateRole(command.ShowOne):
action='store_true',
help=_('Return existing role'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -201,10 +202,12 @@ class CreateRole(command.ShowOne):
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
+ options = common.get_immutable_options(parsed_args)
+
try:
role = identity_client.roles.create(
name=parsed_args.name, domain=domain_id,
- description=parsed_args.description)
+ description=parsed_args.description, options=options)
except ks_exc.Conflict:
if parsed_args.or_show:
@@ -366,6 +369,7 @@ class SetRole(command.Command):
metavar='<name>',
help=_('Set role name'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -376,12 +380,14 @@ class SetRole(command.Command):
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
+ options = common.get_immutable_options(parsed_args)
role = utils.find_resource(identity_client.roles,
parsed_args.role,
domain_id=domain_id)
identity_client.roles.update(role.id, name=parsed_args.name,
- description=parsed_args.description)
+ description=parsed_args.description,
+ options=options)
class ShowRole(command.ShowOne):
diff --git a/openstackclient/network/sdk_utils.py b/openstackclient/network/sdk_utils.py
index af9c74f9..cff30713 100644
--- a/openstackclient/network/sdk_utils.py
+++ b/openstackclient/network/sdk_utils.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import munch
+
def get_osc_show_columns_for_sdk_resource(
sdk_resource,
@@ -38,6 +40,9 @@ def get_osc_show_columns_for_sdk_resource(
# Build the OSC column names to display for the SDK resource.
attr_map = {}
display_columns = list(resource_dict.keys())
+ for col_name in display_columns:
+ if isinstance(resource_dict[col_name], munch.Munch):
+ display_columns.remove(col_name)
invisible_columns = [] if invisible_columns is None else invisible_columns
for col_name in invisible_columns:
if col_name in display_columns:
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 7efbb631..71c1a9af 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -106,7 +100,7 @@ class CreateAddressScope(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_address_scope(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -295,6 +289,6 @@ class ShowAddressScope(command.ShowOne):
parsed_args.address_scope,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index f3e3e5c4..a2765cd1 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -13,7 +13,6 @@
"""IP Floating action implementations"""
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
from osc_lib.utils import tags as _tag
@@ -25,7 +24,6 @@ from openstackclient.network import sdk_utils
_formatters = {
- 'location': format_columns.DictColumn,
'port_details': utils.format_dict,
}
diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py
index f94bcc06..06b3df8b 100644
--- a/openstackclient/network/v2/floating_ip_port_forwarding.py
+++ b/openstackclient/network/v2/floating_ip_port_forwarding.py
@@ -75,6 +75,12 @@ class CreateFloatingIPPortForwarding(command.ShowOne):
required=True,
help=_("The protocol used in the floating IP "
"port forwarding, for instance: TCP, UDP")
+ ),
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A text to describe/contextualize the use of the "
+ "port forwarding configuration")
)
parser.add_argument(
'floating_ip',
@@ -113,6 +119,9 @@ class CreateFloatingIPPortForwarding(command.ShowOne):
attrs['internal_ip_address'] = parsed_args.internal_ip_address
attrs['protocol'] = parsed_args.protocol
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+
obj = client.create_floating_ip_port_forwarding(
floating_ip.id,
**attrs
@@ -212,6 +221,7 @@ class ListFloatingIPPortForwarding(command.Lister):
'internal_port',
'external_port',
'protocol',
+ 'description',
)
headers = (
'ID',
@@ -220,6 +230,7 @@ class ListFloatingIPPortForwarding(command.Lister):
'Internal Port',
'External Port',
'Protocol',
+ 'Description',
)
query = {}
@@ -296,6 +307,12 @@ class SetFloatingIPPortForwarding(command.Command):
metavar='<protocol>',
choices=['tcp', 'udp'],
help=_("The IP protocol used in the floating IP port forwarding")
+ ),
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A text to describe/contextualize the use of "
+ "the port forwarding configuration")
)
return parser
@@ -332,6 +349,9 @@ class SetFloatingIPPortForwarding(command.Command):
if parsed_args.protocol:
attrs['protocol'] = parsed_args.protocol
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+
client.update_floating_ip_port_forwarding(
floating_ip.id, parsed_args.port_forwarding_id, **attrs)
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index c026baa0..ddc88e55 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -21,9 +21,7 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils
-
_formatters = {
- 'location': format_columns.DictColumn,
'subnet_ip_availability': format_columns.ListDictColumn,
}
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 3f579b6d..7a12d523 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -16,7 +16,6 @@
from cliff import columns as cliff_columns
from osc_lib.cli import format_columns
from osc_lib.command import command
-from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
@@ -41,7 +40,6 @@ _formatters = {
'subnet_ids': format_columns.ListColumn,
'admin_state_up': AdminStateColumn,
'is_admin_state_up': AdminStateColumn,
- 'location': format_columns.DictColumn,
'router:external': RouterExternalColumn,
'is_router_external': RouterExternalColumn,
'availability_zones': format_columns.ListColumn,
@@ -126,9 +124,6 @@ def _get_attrs_network(client_manager, parsed_args):
attrs['is_default'] = False
if parsed_args.default:
attrs['is_default'] = True
- if attrs.get('is_default') and not attrs.get('router:external'):
- msg = _("Cannot set default for internal network")
- raise exceptions.CommandError(msg)
# Update Provider network options
if parsed_args.provider_network_type:
attrs['provider:network_type'] = parsed_args.provider_network_type
@@ -706,8 +701,7 @@ class SetNetwork(command.Command):
default_router_grp.add_argument(
'--default',
action='store_true',
- help=_("Set the network as the default external network "
- "(cannot be used with internal network).")
+ help=_("Set the network as the default external network")
)
default_router_grp.add_argument(
'--no-default',
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index a6ed3629..16784854 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -43,7 +43,6 @@ _formatters = {
'alive': AliveColumn,
'admin_state_up': AdminStateColumn,
'is_admin_state_up': AdminStateColumn,
- 'location': format_columns.DictColumn,
'configurations': format_columns.DictColumn,
}
diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py
index f6070a02..36f39200 100644
--- a/openstackclient/network/v2/network_auto_allocated_topology.py
+++ b/openstackclient/network/v2/network_auto_allocated_topology.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -99,17 +93,16 @@ class CreateAutoAllocatedTopology(command.ShowOne):
obj = client.validate_auto_allocated_topology(parsed_args.project)
columns = _format_check_resource_columns()
- data = utils.get_item_properties(
- _format_check_resource(obj),
- columns,
- formatters=_formatters,
- )
+ data = utils.get_item_properties(_format_check_resource(obj),
+ columns,
+ formatters={})
+
return (columns, data)
def get_topology(self, client, parsed_args):
obj = client.get_auto_allocated_topology(parsed_args.project)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
def take_action(self, parsed_args):
diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py
index 355d04c1..c9d368bf 100644
--- a/openstackclient/network/v2/network_flavor.py
+++ b/openstackclient/network/v2/network_flavor.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -142,7 +136,7 @@ class CreateNetworkFlavor(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_flavor(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -306,5 +300,5 @@ class ShowNetworkFlavor(command.ShowOne):
client = self.app.client_manager.network
obj = client.find_flavor(parsed_args.flavor, ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py
index 492fd432..6cf0c412 100644
--- a/openstackclient/network/v2/network_flavor_profile.py
+++ b/openstackclient/network/v2/network_flavor_profile.py
@@ -13,7 +13,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -116,7 +110,7 @@ class CreateNetworkFlavorProfile(command.ShowOne):
obj = client.create_service_profile(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -252,5 +246,5 @@ class ShowNetworkFlavorProfile(command.ShowOne):
obj = client.find_service_profile(parsed_args.flavor_profile,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py
index cde7a304..df0e1da1 100644
--- a/openstackclient/network/v2/network_meter.py
+++ b/openstackclient/network/v2/network_meter.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -108,7 +102,7 @@ class CreateMeter(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -192,5 +186,5 @@ class ShowMeter(command.ShowOne):
obj = client.find_metering_label(parsed_args.meter,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py
index 5f31255a..49ff9e1b 100644
--- a/openstackclient/network/v2/network_meter_rule.py
+++ b/openstackclient/network/v2/network_meter_rule.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -122,7 +116,7 @@ class CreateMeterRule(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -205,5 +199,5 @@ class ShowMeterRule(command.ShowOne):
obj = client.find_metering_label_rule(parsed_args.meter_rule_id,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py
index 1622de4a..fd5ff937 100644
--- a/openstackclient/network/v2/network_qos_policy.py
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -125,7 +119,7 @@ class CreateNetworkQosPolicy(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_qos_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -285,5 +279,5 @@ class ShowNetworkQosPolicy(command.ShowOne):
obj = client.find_qos_policy(parsed_args.policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index d74beda7..28c5600a 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -15,7 +15,6 @@
import itertools
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -47,11 +46,6 @@ ACTION_SET = 'update'
ACTION_SHOW = 'get'
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -214,7 +208,7 @@ class CreateNetworkQosRule(command.ShowOne):
msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
@@ -364,5 +358,5 @@ class ShowNetworkQosRule(command.ShowOne):
{'rule': rule_id, 'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
index e842944c..7b92c8ad 100644
--- a/openstackclient/network/v2/network_qos_rule_type.py
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -21,11 +20,6 @@ from openstackclient.i18n import _
from openstackclient.network import sdk_utils
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
"type": "rule_type_name",
@@ -71,5 +65,5 @@ class ShowNetworkQosRuleType(command.ShowOne):
client = self.app.client_manager.network
obj = client.get_qos_rule_type(parsed_args.rule_type)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index 1781193f..b88ef019 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'target_tenant': 'target_project_id',
@@ -58,6 +52,15 @@ def _get_attrs(client_manager, parsed_args):
object_id = network_client.find_security_group(
parsed_args.rbac_object,
ignore_missing=False).id
+ if parsed_args.type == 'address_scope':
+ object_id = network_client.find_address_scope(
+ parsed_args.rbac_object,
+ ignore_missing=False).id
+ if parsed_args.type == 'subnetpool':
+ object_id = network_client.find_subnet_pool(
+ parsed_args.rbac_object,
+ ignore_missing=False).id
+
attrs['object_id'] = object_id
identity_client = client_manager.identity
@@ -97,9 +100,11 @@ class CreateNetworkRBAC(command.ShowOne):
'--type',
metavar="<type>",
required=True,
- choices=['security_group', 'qos_policy', 'network'],
+ choices=['address_scope', 'security_group', 'subnetpool',
+ 'qos_policy', 'network'],
help=_('Type of the object that RBAC policy '
- 'affects ("security_group", "qos_policy" or "network")')
+ 'affects ("address_scope", "security_group", "subnetpool",'
+ ' "qos_policy" or "network")')
)
parser.add_argument(
'--action',
@@ -142,7 +147,7 @@ class CreateNetworkRBAC(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_rbac_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
@@ -188,10 +193,11 @@ class ListNetworkRBAC(command.Lister):
parser.add_argument(
'--type',
metavar='<type>',
- choices=['security_group', 'qos_policy', 'network'],
+ choices=['address_scope', 'security_group', 'subnetpool',
+ 'qos_policy', 'network'],
help=_('List network RBAC policies according to '
- 'given object type ("security_group", "qos_policy" '
- 'or "network")')
+ 'given object type ("address_scope", "security_group", '
+ '"subnetpool", "qos_policy" or "network")')
)
parser.add_argument(
'--action',
@@ -299,5 +305,5 @@ class ShowNetworkRBAC(command.ShowOne):
obj = client.find_rbac_policy(parsed_args.rbac_policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index 5899dc69..c1a672e2 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
@@ -96,7 +90,7 @@ class CreateNetworkSegment(command.ShowOne):
attrs['segmentation_id'] = parsed_args.segment
obj = client.create_segment(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
@@ -248,5 +242,5 @@ class ShowNetworkSegment(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index b38c72c2..6229995a 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -19,7 +19,6 @@
import itertools
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -31,10 +30,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
@@ -216,7 +211,7 @@ class CreateNetworkSegmentRange(command.ShowOne):
attrs['physical_network'] = parsed_args.physical_network
obj = network_client.create_network_segment_range(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
@@ -455,6 +450,6 @@ class ShowNetworkSegmentRange(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 4011f5f0..a21324ae 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -51,7 +51,6 @@ _formatters = {
'dns_assignment': format_columns.ListDictColumn,
'extra_dhcp_opts': format_columns.ListDictColumn,
'fixed_ips': format_columns.ListDictColumn,
- 'location': format_columns.DictColumn,
'security_group_ids': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 464dbbec..e3e8accd 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -61,7 +61,6 @@ _formatters = {
'external_gateway_info': RouterInfoColumn,
'availability_zones': format_columns.ListColumn,
'availability_zone_hints': format_columns.ListColumn,
- 'location': format_columns.DictColumn,
'routes': RoutesColumn,
'tags': format_columns.ListColumn,
}
@@ -168,6 +167,93 @@ class AddSubnetToRouter(command.Command):
subnet_id=subnet.id)
+class AddExtraRoutesToRouter(command.ShowOne):
+ _description = _("Add extra static routes to a router's routing table.")
+
+ def get_parser(self, prog_name):
+ parser = super(AddExtraRoutesToRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router to which extra static routes "
+ "will be added (name or ID).")
+ )
+ parser.add_argument(
+ '--route',
+ metavar='destination=<subnet>,gateway=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ dest='routes',
+ default=[],
+ required_keys=['destination', 'gateway'],
+ help=_("Add extra static route to the router. "
+ "destination: destination subnet (in CIDR notation), "
+ "gateway: nexthop IP address. "
+ "Repeat option to add multiple routes. "
+ "Trying to add a route that's already present "
+ "(exactly, including destination and nexthop) "
+ "in the routing table is allowed and is considered "
+ "a successful operation.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ if parsed_args.routes is not None:
+ for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
+ client = self.app.client_manager.network
+ router_obj = client.add_extra_routes_to_router(
+ client.find_router(parsed_args.router, ignore_missing=False),
+ body={'router': {'routes': parsed_args.routes}})
+ display_columns, columns = _get_columns(router_obj)
+ data = utils.get_item_properties(
+ router_obj, columns, formatters=_formatters)
+ return (display_columns, data)
+
+
+class RemoveExtraRoutesFromRouter(command.ShowOne):
+ _description = _(
+ "Remove extra static routes from a router's routing table.")
+
+ def get_parser(self, prog_name):
+ parser = super(RemoveExtraRoutesFromRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router from which extra static routes "
+ "will be removed (name or ID).")
+ )
+ parser.add_argument(
+ '--route',
+ metavar='destination=<subnet>,gateway=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ dest='routes',
+ default=[],
+ required_keys=['destination', 'gateway'],
+ help=_("Remove extra static route from the router. "
+ "destination: destination subnet (in CIDR notation), "
+ "gateway: nexthop IP address. "
+ "Repeat option to remove multiple routes. "
+ "Trying to remove a route that's already missing "
+ "(fully, including destination and nexthop) "
+ "from the routing table is allowed and is considered "
+ "a successful operation.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ if parsed_args.routes is not None:
+ for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
+ client = self.app.client_manager.network
+ router_obj = client.remove_extra_routes_from_router(
+ client.find_router(parsed_args.router, ignore_missing=False),
+ body={'router': {'routes': parsed_args.routes}})
+ display_columns, columns = _get_columns(router_obj)
+ data = utils.get_item_properties(
+ router_obj, columns, formatters=_formatters)
+ return (display_columns, data)
+
+
# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
# OSC minimum requirements include SDK 1.0.
class CreateRouter(command.ShowOne):
@@ -540,17 +626,21 @@ class SetRouter(command.Command):
dest='routes',
default=None,
required_keys=['destination', 'gateway'],
- help=_("Routes associated with the router "
+ help=_("Add routes to the router "
"destination: destination subnet (in CIDR notation) "
"gateway: nexthop IP address "
- "(repeat option to set multiple routes)")
+ "(repeat option to add multiple routes). "
+ "This is deprecated in favor of 'router add/remove route' "
+ "since it is prone to race conditions between concurrent "
+ "clients when not used together with --no-route to "
+ "overwrite the current value of 'routes'.")
)
parser.add_argument(
'--no-route',
action='store_true',
help=_("Clear routes associated with the router. "
"Specify both --route and --no-route to overwrite "
- "current value of route.")
+ "current value of routes.")
)
routes_ha = parser.add_mutually_exclusive_group()
routes_ha.add_argument(
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 2033af14..0732c23e 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -16,7 +16,6 @@
import argparse
from cliff import columns as cliff_columns
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
from osc_lib.utils import tags as _tag
@@ -77,13 +76,11 @@ class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn):
_formatters_network = {
- 'location': format_columns.DictColumn,
'security_group_rules': NetworkSecurityGroupRulesColumn,
}
_formatters_compute = {
- 'location': format_columns.DictColumn,
'rules': ComputeSecurityGroupRulesColumn,
}
@@ -120,6 +117,19 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
metavar='<project>',
help=self.enhance_help_neutron(_("Owner's project (name or ID)"))
)
+ stateful_group = parser.add_mutually_exclusive_group()
+ stateful_group.add_argument(
+ "--stateful",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateful (Default)")
+ )
+ stateful_group.add_argument(
+ "--stateless",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateless")
+ )
identity_common.add_project_domain_option_to_parser(
parser, enhance_help=self.enhance_help_neutron)
_tag.add_tag_option_to_parser_for_create(
@@ -138,6 +148,10 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
attrs = {}
attrs['name'] = parsed_args.name
attrs['description'] = self._get_description(parsed_args)
+ if parsed_args.stateful:
+ attrs['stateful'] = True
+ if parsed_args.stateless:
+ attrs['stateful'] = False
if parsed_args.project is not None:
identity_client = self.app.client_manager.identity
project_id = identity_common.find_project(
@@ -315,6 +329,19 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
metavar="<description>",
help=_("New security group description")
)
+ stateful_group = parser.add_mutually_exclusive_group()
+ stateful_group.add_argument(
+ "--stateful",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateful (Default)")
+ )
+ stateful_group.add_argument(
+ "--stateless",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateless")
+ )
return parser
def update_parser_network(self, parser):
@@ -331,6 +358,10 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
attrs['name'] = parsed_args.name
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
+ if parsed_args.stateful:
+ attrs['stateful'] = True
+ if parsed_args.stateless:
+ attrs['stateful'] = False
# NOTE(rtheis): Previous behavior did not raise a CommandError
# if there were no updates. Maintain this behavior and issue
# the update.
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index f48478ea..1fbd97ab 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -16,7 +16,6 @@
import argparse
import logging
-from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib import exceptions
from osc_lib import utils
@@ -31,11 +30,6 @@ from openstackclient.network import utils as network_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _format_security_group_rule_show(obj):
data = network_utils.transform_compute_security_group_rule(obj)
return zip(*sorted(data.items()))
@@ -353,7 +347,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
# Create and show the security group rule.
obj = client.create_security_group_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
@@ -620,7 +614,7 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
if not obj['remote_ip_prefix']:
obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index f6844065..f87f7abe 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -61,7 +61,6 @@ _formatters = {
'allocation_pools': AllocationPoolsColumn,
'dns_nameservers': format_columns.ListColumn,
'host_routes': HostRoutesColumn,
- 'location': format_columns.DictColumn,
'service_types': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 2750574a..56cf6152 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -42,7 +42,6 @@ def _get_columns(item):
_formatters = {
- 'location': format_columns.DictColumn,
'prefixes': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py
index 96d41c3a..27cf4481 100644
--- a/openstackclient/tests/functional/identity/v3/test_project.py
+++ b/openstackclient/tests/functional/identity/v3/test_project.py
@@ -79,7 +79,6 @@ class ProjectTests(common.IdentityTests):
'--disable '
'--property k0=v0 '
'%(name)s' % {'new_name': new_project_name,
- 'domain': self.domain_name,
'name': project_name})
self.assertEqual(0, len(raw_output))
# check project details
diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py
index 05aad7a0..0769dca6 100644
--- a/openstackclient/tests/functional/network/v2/test_router.py
+++ b/openstackclient/tests/functional/network/v2/test_router.py
@@ -261,3 +261,46 @@ class RouterTests(common.NetworkTagTests):
new_name
))
self.assertIsNone(cmd_output["external_gateway_info"])
+
+ def test_router_add_remove_route(self):
+ network_name = uuid.uuid4().hex
+ subnet_name = uuid.uuid4().hex
+ router_name = uuid.uuid4().hex
+
+ self.openstack('network create %s' % network_name)
+ self.addCleanup(self.openstack, 'network delete %s' % network_name)
+
+ self.openstack(
+ 'subnet create %s '
+ '--network %s --subnet-range 10.0.0.0/24' % (
+ subnet_name, network_name))
+
+ self.openstack('router create %s' % router_name)
+ self.addCleanup(self.openstack, 'router delete %s' % router_name)
+
+ self.openstack('router add subnet %s %s' % (router_name, subnet_name))
+ self.addCleanup(self.openstack, 'router remove subnet %s %s' % (
+ router_name, subnet_name))
+
+ out1 = json.loads(self.openstack(
+ 'router add route -f json %s '
+ '--route destination=10.0.10.0/24,gateway=10.0.0.10' %
+ router_name)),
+ self.assertEqual(1, len(out1[0]['routes']))
+
+ self.addCleanup(
+ self.openstack, 'router set %s --no-route' % router_name)
+
+ out2 = json.loads(self.openstack(
+ 'router add route -f json %s '
+ '--route destination=10.0.10.0/24,gateway=10.0.0.10 '
+ '--route destination=10.0.11.0/24,gateway=10.0.0.11' %
+ router_name)),
+ self.assertEqual(2, len(out2[0]['routes']))
+
+ out3 = json.loads(self.openstack(
+ 'router remove route -f json %s '
+ '--route destination=10.0.11.0/24,gateway=10.0.0.11 '
+ '--route destination=10.0.12.0/24,gateway=10.0.0.12' %
+ router_name)),
+ self.assertEqual(1, len(out3[0]['routes']))
diff --git a/openstackclient/tests/functional/network/v2/test_security_group.py b/openstackclient/tests/functional/network/v2/test_security_group.py
index 8ae24b72..d46f8db7 100644
--- a/openstackclient/tests/functional/network/v2/test_security_group.py
+++ b/openstackclient/tests/functional/network/v2/test_security_group.py
@@ -42,7 +42,7 @@ class SecurityGroupTests(common.NetworkTests):
def test_security_group_set(self):
other_name = uuid.uuid4().hex
raw_output = self.openstack(
- 'security group set --description NSA --name ' +
+ 'security group set --description NSA --stateless --name ' +
other_name + ' ' + self.NAME
)
self.assertEqual('', raw_output)
@@ -50,8 +50,10 @@ class SecurityGroupTests(common.NetworkTests):
cmd_output = json.loads(self.openstack(
'security group show -f json ' + other_name))
self.assertEqual('NSA', cmd_output['description'])
+ self.assertFalse(cmd_output['stateful'])
def test_security_group_show(self):
cmd_output = json.loads(self.openstack(
'security group show -f json ' + self.NAME))
self.assertEqual(self.NAME, cmd_output['name'])
+ self.assertTrue(cmd_output['stateful'])
diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py
index 96c68d5a..b9e0740c 100644
--- a/openstackclient/tests/unit/api/test_object_store_v1.py
+++ b/openstackclient/tests/unit/api/test_object_store_v1.py
@@ -30,8 +30,10 @@ FAKE_CONTAINER = 'rainbarrel'
FAKE_OBJECT = 'spigot'
LIST_CONTAINER_RESP = [
- 'qaz',
- 'fred',
+ {"name": "qaz", "count": 0, "bytes": 0,
+ "last_modified": "2020-05-16T05:52:07.377550"},
+ {"name": "fred", "count": 0, "bytes": 0,
+ "last_modified": "2020-05-16T05:55:07.377550"},
]
LIST_OBJECT_RESP = [
@@ -117,34 +119,32 @@ class TestContainer(TestObjectAPIv1):
)
self.assertEqual(LIST_CONTAINER_RESP, ret)
-# def test_container_list_full_listing(self):
-# sess = self.app.client_manager.session
-#
-# def side_effect(*args, **kwargs):
-# rv = sess.get().json.return_value
-# sess.get().json.return_value = []
-# sess.get().json.side_effect = None
-# return rv
-#
-# resp = [{'name': 'is-name'}]
-# sess.get().json.return_value = resp
-# sess.get().json.side_effect = side_effect
-#
-# data = lib_container.list_containers(
-# self.app.client_manager.session,
-# fake_url,
-# full_listing=True,
-# )
-#
-# # Check expected values
-# sess.get.assert_called_with(
-# fake_url,
-# params={
-# 'format': 'json',
-# 'marker': 'is-name',
-# }
-# )
-# self.assertEqual(resp, data)
+ def test_container_list_full_listing(self):
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '?limit=1&format=json',
+ json=[LIST_CONTAINER_RESP[0]],
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL +
+ '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[0]['name'],
+ json=[LIST_CONTAINER_RESP[1]],
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL +
+ '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[1]['name'],
+ json=[],
+ status_code=200,
+ )
+ ret = self.api.container_list(
+ limit=1,
+ full_listing=True,
+ )
+ self.assertEqual(LIST_CONTAINER_RESP, ret)
def test_container_show(self):
headers = {
diff --git a/openstackclient/tests/unit/common/test_parseractions.py b/openstackclient/tests/unit/common/test_parseractions.py
index d015da43..736cd0b6 100644
--- a/openstackclient/tests/unit/common/test_parseractions.py
+++ b/openstackclient/tests/unit/common/test_parseractions.py
@@ -92,7 +92,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- self.assertItemsEqual(expect, actual)
+ self.assertCountEqual(expect, actual)
def test_empty_required_optional(self):
self.parser.add_argument(
@@ -116,7 +116,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- self.assertItemsEqual(expect, actual)
+ self.assertCountEqual(expect, actual)
def test_error_values_with_comma(self):
self.assertRaises(
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 8ec8217d..7e4c71c5 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -3577,6 +3577,41 @@ class TestServerRebuild(TestServer):
self.cmd = server.RebuildServer(self.app, None)
+ def test_rebuild_with_image_name(self):
+ image_name = 'my-custom-image'
+ user_image = image_fakes.FakeImage.create_one_image(
+ attrs={'name': image_name})
+ self.find_image_mock.return_value = user_image
+
+ attrs = {
+ 'image': {
+ 'id': user_image.id
+ },
+ 'networks': {},
+ 'adminPass': 'passw0rd',
+ }
+ new_server = compute_fakes.FakeServer.create_one_server(attrs=attrs)
+ self.server.rebuild.return_value = new_server
+
+ arglist = [
+ self.server.id,
+ '--image', image_name
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ('image', image_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # Get the command object to test.
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.find_image_mock.assert_called_with(
+ image_name, ignore_missing=False)
+ self.get_image_mock.assert_called_with(user_image.id)
+ self.server.rebuild.assert_called_with(user_image, None)
+
def test_rebuild_with_current_image(self):
arglist = [
self.server.id,
@@ -3590,6 +3625,7 @@ class TestServerRebuild(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id)
+ self.find_image_mock.assert_not_called()
self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, None)
diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py
index f8b6093a..904fe323 100644
--- a/openstackclient/tests/unit/identity/v3/test_access_rule.py
+++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py
@@ -14,8 +14,8 @@
#
import copy
+from unittest import mock
-import mock
from osc_lib import exceptions
from osc_lib import utils
diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py
index 014986e5..46f389e8 100644
--- a/openstackclient/tests/unit/identity/v3/test_domain.py
+++ b/openstackclient/tests/unit/identity/v3/test_domain.py
@@ -68,6 +68,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -97,6 +98,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': 'new desc',
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -126,6 +128,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -155,6 +158,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': False,
}
self.domains_mock.create.assert_called_with(
@@ -164,6 +168,66 @@ class TestDomainCreate(TestDomain):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_domain_create_with_immutable(self):
+ arglist = [
+ '--immutable',
+ self.domain.name,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('name', self.domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.domain.name,
+ 'description': None,
+ 'options': {'immutable': True},
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_domain_create_with_no_immutable(self):
+ arglist = [
+ '--no-immutable',
+ self.domain.name,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('name', self.domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.domain.name,
+ 'description': None,
+ 'options': {'immutable': False},
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
class TestDomainDelete(TestDomain):
@@ -354,6 +418,52 @@ class TestDomainSet(TestDomain):
)
self.assertIsNone(result)
+ def test_domain_set_immutable_option(self):
+ arglist = [
+ '--immutable',
+ self.domain.id,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': True},
+ }
+ self.domains_mock.update.assert_called_with(
+ self.domain.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_domain_set_no_immutable_option(self):
+ arglist = [
+ '--no-immutable',
+ self.domain.id,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': False},
+ }
+ self.domains_mock.update.assert_called_with(
+ self.domain.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestDomainShow(TestDomain):
diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py
index 466bea18..dfd0805b 100644
--- a/openstackclient/tests/unit/identity/v3/test_project.py
+++ b/openstackclient/tests/unit/identity/v3/test_project.py
@@ -98,7 +98,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -156,7 +157,8 @@ class TestProjectCreate(TestProject):
'description': 'new desc',
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -194,7 +196,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -232,7 +235,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
self.projects_mock.create.assert_called_with(
**kwargs
@@ -266,7 +270,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -302,7 +307,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': False,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=,
# description=, enabled=, **kwargs)
@@ -339,7 +345,8 @@ class TestProjectCreate(TestProject):
'parent': None,
'fee': 'fi',
'fo': 'fum',
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -350,6 +357,126 @@ class TestProjectCreate(TestProject):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_project_create_is_domain_false_property(self):
+ arglist = [
+ '--property', 'is_domain=false',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'false'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': False,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_is_domain_true_property(self):
+ arglist = [
+ '--property', 'is_domain=true',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'true'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': True,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_is_domain_none_property(self):
+ arglist = [
+ '--property', 'is_domain=none',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'none'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': None,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
def test_project_create_parent(self):
self.parent = identity_fakes.FakeProject.create_one_project()
self.project = identity_fakes.FakeProject.create_one_project(
@@ -380,7 +507,8 @@ class TestProjectCreate(TestProject):
'parent': self.parent.id,
'description': None,
'enabled': True,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
self.projects_mock.create.assert_called_with(
@@ -465,8 +593,89 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': ['foo']
+ 'tags': ['foo'],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_with_immutable_option(self):
+ arglist = [
+ '--immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('description', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('parent', None),
+ ('tags', [])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'tags': [],
+ 'options': {'immutable': True},
}
+ # ProjectManager.create(name=, domain=, description=,
+ # enabled=, **kwargs)
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_with_no_immutable_option(self):
+ arglist = [
+ '--no-immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('description', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('parent', None),
+ ('tags', [])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'tags': [],
+ 'options': {'immutable': False},
+ }
+ # ProjectManager.create(name=, domain=, description=,
+ # enabled=, **kwargs)
self.projects_mock.create.assert_called_with(
**kwargs
)
@@ -927,6 +1136,60 @@ class TestProjectSet(TestProject):
)
self.assertIsNone(result)
+ def test_project_set_with_immutable_option(self):
+ arglist = [
+ '--domain', self.project.domain_id,
+ '--immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('domain', self.project.domain_id),
+ ('immutable', True),
+ ('enable', False),
+ ('disable', False),
+ ('project', self.project.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': True},
+ }
+ self.projects_mock.update.assert_called_with(
+ self.project.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_project_set_with_no_immutable_option(self):
+ arglist = [
+ '--domain', self.project.domain_id,
+ '--no-immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('domain', self.project.domain_id),
+ ('no_immutable', True),
+ ('enable', False),
+ ('disable', False),
+ ('project', self.project.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': False},
+ }
+ self.projects_mock.update.assert_called_with(
+ self.project.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestProjectShow(TestProject):
diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py
index 4278ab1c..544da7c1 100644
--- a/openstackclient/tests/unit/identity/v3/test_role.py
+++ b/openstackclient/tests/unit/identity/v3/test_role.py
@@ -333,6 +333,7 @@ class TestRoleCreate(TestRole):
'domain': None,
'name': identity_fakes.role_name,
'description': None,
+ 'options': {},
}
# RoleManager.create(name=, domain=)
@@ -377,6 +378,7 @@ class TestRoleCreate(TestRole):
'domain': identity_fakes.domain_id,
'name': identity_fakes.ROLE_2['name'],
'description': None,
+ 'options': {},
}
# RoleManager.create(name=, domain=)
@@ -420,6 +422,97 @@ class TestRoleCreate(TestRole):
'description': identity_fakes.role_description,
'name': identity_fakes.ROLE_2['name'],
'domain': None,
+ 'options': {},
+ }
+
+ # RoleManager.create(name=, domain=)
+ self.roles_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('domain', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ 'd1',
+ identity_fakes.ROLE_2['id'],
+ identity_fakes.ROLE_2['name'],
+ )
+ self.assertEqual(datalist, data)
+
+ def test_role_create_with_immutable_option(self):
+
+ self.roles_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('name', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+
+ 'options': {'immutable': True},
+ 'description': None,
+ 'name': identity_fakes.ROLE_2['name'],
+ 'domain': None,
+ }
+
+ # RoleManager.create(name=, domain=)
+ self.roles_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('domain', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ 'd1',
+ identity_fakes.ROLE_2['id'],
+ identity_fakes.ROLE_2['name'],
+ )
+ self.assertEqual(datalist, data)
+
+ def test_role_create_with_no_immutable_option(self):
+
+ self.roles_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--no-immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('name', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+
+ 'options': {'immutable': False},
+ 'description': None,
+ 'name': identity_fakes.ROLE_2['name'],
+ 'domain': None,
}
# RoleManager.create(name=, domain=)
@@ -871,6 +964,7 @@ class TestRoleSet(TestRole):
kwargs = {
'name': 'over',
'description': None,
+ 'options': {},
}
# RoleManager.update(role, name=)
self.roles_mock.update.assert_called_with(
@@ -903,6 +997,7 @@ class TestRoleSet(TestRole):
kwargs = {
'name': 'over',
'description': None,
+ 'options': {},
}
# RoleManager.update(role, name=)
self.roles_mock.update.assert_called_with(
@@ -935,6 +1030,73 @@ class TestRoleSet(TestRole):
kwargs = {
'name': 'over',
'description': identity_fakes.role_description,
+ 'options': {},
+ }
+ # RoleManager.update(role, name=)
+ self.roles_mock.update.assert_called_with(
+ identity_fakes.ROLE_2['id'],
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_role_set_with_immutable(self):
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--name', 'over',
+ '--immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('name', 'over'),
+ ('immutable', True),
+ ('role', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'over',
+ 'description': None,
+ 'options': {'immutable': True},
+ }
+ # RoleManager.update(role, name=)
+ self.roles_mock.update.assert_called_with(
+ identity_fakes.ROLE_2['id'],
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_role_set_with_no_immutable(self):
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--name', 'over',
+ '--no-immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('name', 'over'),
+ ('no_immutable', True),
+ ('role', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'over',
+ 'description': None,
+ 'options': {'immutable': False},
}
# RoleManager.update(role, name=)
self.roles_mock.update.assert_called_with(
diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py
index 0c98a129..5788b473 100644
--- a/openstackclient/tests/unit/integ/cli/test_shell.py
+++ b/openstackclient/tests/unit/integ/cli/test_shell.py
@@ -20,16 +20,6 @@ from openstackclient import shell
from openstackclient.tests.unit.integ import base as test_base
from openstackclient.tests.unit import test_shell
-# NOTE(dtroyer): Attempt the import to detect if the SDK installed is new
-# enough to contain the os_client_config code. If so, use
-# that path for mocks.
-CONFIG_MOCK_BASE = "openstack.config.loader"
-try:
- from openstack.config import defaults # noqa
-except ImportError:
- # Fall back to os-client-config
- CONFIG_MOCK_BASE = "os_client_config.config"
-
class TestIntegShellCliNoAuth(test_base.TestInteg):
@@ -455,8 +445,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
temp_dir = self.useFixture(fixtures.TempDir())
return temp_dir.join(filename)
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file")
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file")
def test_shell_args_precedence_1(self, config_mock, vendor_mock):
"""Precedence run 1
@@ -473,7 +463,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1))
vendor_mock.side_effect = vendor_mock_return
- print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE)
_shell = shell.OpenStackShell()
_shell.run(
"--os-password qaz extension list".split(),
@@ -527,8 +516,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
# +env, +cli, +occ
# see test_shell_args_precedence_2()
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file")
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file")
def test_shell_args_precedence_2(self, config_mock, vendor_mock):
"""Precedence run 2
@@ -545,7 +534,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1))
vendor_mock.side_effect = vendor_mock_return
- print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE)
_shell = shell.OpenStackShell()
_shell.run(
"--os-username zarquon --os-password qaz "
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index a553f501..cef0a11c 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -1227,6 +1227,7 @@ class FakeSecurityGroup(object):
'id': 'security-group-id-' + uuid.uuid4().hex,
'name': 'security-group-name-' + uuid.uuid4().hex,
'description': 'security-group-description-' + uuid.uuid4().hex,
+ 'stateful': True,
'project_id': 'project-id-' + uuid.uuid4().hex,
'security_group_rules': [],
'tags': []
@@ -1832,7 +1833,7 @@ class FakeFloatingIPPortForwarding(object):
"""
attrs = attrs or {}
floatingip_id = (
- attrs.get('floatingip_id') or'floating-ip-id-' + uuid.uuid4().hex
+ attrs.get('floatingip_id') or 'floating-ip-id-' + uuid.uuid4().hex
)
# Set default attributes.
port_forwarding_attrs = {
@@ -1843,6 +1844,7 @@ class FakeFloatingIPPortForwarding(object):
'internal_port': randint(1, 65535),
'external_port': randint(1, 65535),
'protocol': 'tcp',
+ 'description': 'some description',
}
# Overwrite default attributes.
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
index ea6cdd26..1028c18a 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
@@ -62,6 +62,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self.app, self.namespace)
self.columns = (
+ 'description',
'external_port',
'floatingip_id',
'id',
@@ -73,6 +74,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
)
self.data = (
+ self.new_port_forwarding.description,
self.new_port_forwarding.external_port,
self.new_port_forwarding.floatingip_id,
self.new_port_forwarding.id,
@@ -102,6 +104,8 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self.new_port_forwarding.floatingip_id,
'--internal-ip-address',
self.new_port_forwarding.internal_ip_address,
+ '--description',
+ self.new_port_forwarding.description,
]
verifylist = [
('port', self.new_port_forwarding.internal_port_id),
@@ -111,6 +115,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
('floating_ip', self.new_port_forwarding.floatingip_id),
('internal_ip_address', self.new_port_forwarding.
internal_ip_address),
+ ('description', self.new_port_forwarding.description),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
@@ -126,6 +131,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_port_id': self.new_port_forwarding.
internal_port_id,
'protocol': self.new_port_forwarding.protocol,
+ 'description': self.new_port_forwarding.description,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -251,7 +257,8 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'Internal IP Address',
'Internal Port',
'External Port',
- 'Protocol'
+ 'Protocol',
+ 'Description',
)
def setUp(self):
@@ -273,6 +280,7 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
port_forwarding.internal_port,
port_forwarding.external_port,
port_forwarding.protocol,
+ port_forwarding.description,
))
self.network.floating_ip_port_forwardings = mock.Mock(
return_value=self.port_forwardings
@@ -393,6 +401,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'--internal-protocol-port', '100',
'--external-protocol-port', '200',
'--protocol', 'tcp',
+ '--description', 'some description',
self._port_forwarding.floatingip_id,
self._port_forwarding.id,
]
@@ -402,6 +411,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
('internal_protocol_port', 100),
('external_protocol_port', 200),
('protocol', 'tcp'),
+ ('description', 'some description'),
('floating_ip', self._port_forwarding.floatingip_id),
('port_forwarding_id', self._port_forwarding.id),
]
@@ -415,6 +425,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_port': 100,
'external_port': 200,
'protocol': 'tcp',
+ 'description': 'some description',
}
self.network.update_floating_ip_port_forwarding.assert_called_with(
self._port_forwarding.floatingip_id,
@@ -428,6 +439,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
# The port forwarding to show.
columns = (
+ 'description',
'external_port',
'floatingip_id',
'id',
@@ -450,6 +462,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
)
)
self.data = (
+ self._port_forwarding.description,
self._port_forwarding.external_port,
self._port_forwarding.floatingip_id,
self._port_forwarding.id,
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 45d6008b..5f8eed67 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -278,24 +278,6 @@ class TestCreateNetworkIdentityV3(TestNetwork):
def test_create_with_no_tag(self):
self._test_create_with_tag(add_tags=False)
- def test_create_default_internal(self):
- arglist = [
- self._network.name,
- "--default",
- ]
- verifylist = [
- ('name', self._network.name),
- ('enable', True),
- ('share', None),
- ('project', None),
- ('external', False),
- ('default', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
-
class TestCreateNetworkIdentityV2(TestNetwork):
@@ -1043,21 +1025,6 @@ class TestSetNetwork(TestNetwork):
def test_set_with_no_tag(self):
self._test_set_tags(with_tags=False)
- def test_set_default_internal(self):
- arglist = [
- self._network.name,
- '--internal',
- '--default',
- ]
- verifylist = [
- ('internal', True),
- ('default', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
-
class TestShowNetwork(TestNetwork):
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index 078188ce..d7c71ea7 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -14,6 +14,7 @@
from unittest import mock
from unittest.mock import call
+import ddt
from osc_lib import exceptions
from openstackclient.network.v2 import network_rbac
@@ -33,11 +34,14 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2):
self.projects_mock = self.app.client_manager.identity.projects
+@ddt.ddt
class TestCreateNetworkRBAC(TestNetworkRBAC):
network_object = network_fakes.FakeNetwork.create_one_network()
qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group()
+ as_object = network_fakes.FakeAddressScope.create_one_address_scope()
+ snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool()
project = identity_fakes_v3.FakeProject.create_one_project()
rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
attrs={'tenant_id': project.id,
@@ -77,6 +81,10 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
return_value=self.qos_object)
self.network.find_security_group = mock.Mock(
return_value=self.sg_object)
+ self.network.find_address_scope = mock.Mock(
+ return_value=self.as_object)
+ self.network.find_subnet_pool = mock.Mock(
+ return_value=self.snp_object)
self.projects_mock.get.return_value = self.project
def test_network_rbac_create_no_type(self):
@@ -224,57 +232,29 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
- def test_network_rbac_create_qos_object(self):
- self.rbac_policy.object_type = 'qos_policy'
- self.rbac_policy.object_id = self.qos_object.id
- arglist = [
- '--type', 'qos_policy',
- '--action', self.rbac_policy.action,
- '--target-project', self.rbac_policy.target_tenant,
- self.qos_object.name,
- ]
- verifylist = [
- ('type', 'qos_policy'),
- ('action', self.rbac_policy.action),
- ('target_project', self.rbac_policy.target_tenant),
- ('rbac_object', self.qos_object.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # DisplayCommandBase.take_action() returns two tuples
- columns, data = self.cmd.take_action(parsed_args)
-
- self.network.create_rbac_policy.assert_called_with(**{
- 'object_id': self.qos_object.id,
- 'object_type': 'qos_policy',
- 'action': self.rbac_policy.action,
- 'target_tenant': self.rbac_policy.target_tenant,
- })
- self.data = [
- self.rbac_policy.action,
- self.rbac_policy.id,
- self.qos_object.id,
- 'qos_policy',
- self.rbac_policy.tenant_id,
- self.rbac_policy.target_tenant,
- ]
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ @ddt.data(
+ ('qos_policy', "qos_object"),
+ ('security_group', "sg_object"),
+ ('subnetpool', "snp_object"),
+ ('address_scope', "as_object")
+ )
+ @ddt.unpack
+ def test_network_rbac_create_object(self, obj_type, obj_fake_attr):
+ obj_fake = getattr(self, obj_fake_attr)
- def test_network_rbac_create_security_group_object(self):
- self.rbac_policy.object_type = 'security_group'
- self.rbac_policy.object_id = self.sg_object.id
+ self.rbac_policy.object_type = obj_type
+ self.rbac_policy.object_id = obj_fake.id
arglist = [
- '--type', 'security_group',
+ '--type', obj_type,
'--action', self.rbac_policy.action,
'--target-project', self.rbac_policy.target_tenant,
- self.sg_object.name,
+ obj_fake.name,
]
verifylist = [
- ('type', 'security_group'),
+ ('type', obj_type),
('action', self.rbac_policy.action),
('target_project', self.rbac_policy.target_tenant),
- ('rbac_object', self.sg_object.name),
+ ('rbac_object', obj_fake.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -282,16 +262,16 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
columns, data = self.cmd.take_action(parsed_args)
self.network.create_rbac_policy.assert_called_with(**{
- 'object_id': self.sg_object.id,
- 'object_type': 'security_group',
+ 'object_id': obj_fake.id,
+ 'object_type': obj_type,
'action': self.rbac_policy.action,
'target_tenant': self.rbac_policy.target_tenant,
})
self.data = [
self.rbac_policy.action,
self.rbac_policy.id,
- self.sg_object.id,
- 'security_group',
+ obj_fake.id,
+ obj_type,
self.rbac_policy.tenant_id,
self.rbac_policy.target_tenant,
]
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 38861b0a..09b4957c 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -776,6 +776,146 @@ class TestRemoveSubnetFromRouter(TestRouter):
self.assertIsNone(result)
+class TestAddExtraRoutesToRouter(TestRouter):
+
+ _router = network_fakes.FakeRouter.create_one_router()
+
+ def setUp(self):
+ super(TestAddExtraRoutesToRouter, self).setUp()
+ self.network.add_extra_routes_to_router = mock.Mock(
+ return_value=self._router)
+ self.cmd = router.AddExtraRoutesToRouter(self.app, self.namespace)
+ self.network.find_router = mock.Mock(return_value=self._router)
+
+ def test_add_no_extra_route(self):
+ arglist = [
+ self._router.id,
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': []}})
+ self.assertEqual(2, len(result))
+
+ def test_add_one_extra_route(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+ def test_add_multiple_extra_routes(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ '--route', 'destination=dst2,gateway=gw2',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [
+ {'destination': 'dst1', 'gateway': 'gw1'},
+ {'destination': 'dst2', 'gateway': 'gw2'},
+ ]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ {'destination': 'dst2', 'nexthop': 'gw2'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+
+class TestRemoveExtraRoutesFromRouter(TestRouter):
+
+ _router = network_fakes.FakeRouter.create_one_router()
+
+ def setUp(self):
+ super(TestRemoveExtraRoutesFromRouter, self).setUp()
+ self.network.remove_extra_routes_from_router = mock.Mock(
+ return_value=self._router)
+ self.cmd = router.RemoveExtraRoutesFromRouter(self.app, self.namespace)
+ self.network.find_router = mock.Mock(return_value=self._router)
+
+ def test_remove_no_extra_route(self):
+ arglist = [
+ self._router.id,
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': []}})
+ self.assertEqual(2, len(result))
+
+ def test_remove_one_extra_route(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+ def test_remove_multiple_extra_routes(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ '--route', 'destination=dst2,gateway=gw2',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [
+ {'destination': 'dst1', 'gateway': 'gw1'},
+ {'destination': 'dst2', 'gateway': 'gw2'},
+ ]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ {'destination': 'dst2', 'nexthop': 'gw2'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+
class TestSetRouter(TestRouter):
# The router to set.
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py
index 67908fa8..7c1d7fb6 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py
@@ -49,6 +49,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'stateful',
'tags',
)
@@ -58,6 +59,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.name,
_security_group.project_id,
security_group.NetworkSecurityGroupRulesColumn([]),
+ _security_group.stateful,
_security_group.tags,
)
@@ -101,6 +103,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'--description', self._security_group.description,
'--project', self.project.name,
'--project-domain', self.domain.name,
+ '--stateful',
self._security_group.name,
]
verifylist = [
@@ -108,6 +111,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
('name', self._security_group.name),
('project', self.project.name),
('project_domain', self.domain.name),
+ ('stateful', self._security_group.stateful),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -115,6 +119,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.create_security_group.assert_called_once_with(**{
'description': self._security_group.description,
+ 'stateful': self._security_group.stateful,
'name': self._security_group.name,
'tenant_id': self.project.id,
})
@@ -421,11 +426,13 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
arglist = [
'--name', new_name,
'--description', new_description,
+ '--stateful',
self._security_group.name,
]
verifylist = [
('description', new_description),
('group', self._security_group.name),
+ ('stateful', self._security_group.stateful),
('name', new_name),
]
@@ -435,6 +442,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
attrs = {
'description': new_description,
'name': new_name,
+ 'stateful': True,
}
self.network.update_security_group.assert_called_once_with(
self._security_group,
@@ -489,6 +497,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'stateful',
'tags',
)
@@ -499,6 +508,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.project_id,
security_group.NetworkSecurityGroupRulesColumn(
[_security_group_rule._info]),
+ _security_group.stateful,
_security_group.tags,
)