Network tag support

Neutron tag mechanism now supports network, subnet, port,
subnetpool and router. Tag support for more resources is planned.

This commit introduces a common mixin class to implement
tag operation and individual resource consumes it.

To support tag remove, network unset command is added.

Implements blueprint neutron-client-tag
Change-Id: Iad59d052f46896d27d73c22d6d4bb3df889f2352
This commit is contained in:
Akihiro Motoki 2017-04-29 00:32:32 +00:00
parent e889ba1524
commit 57e5840710
27 changed files with 1172 additions and 102 deletions

View File

@ -32,6 +32,7 @@ Create new network
[--provider-segment <provider-segment>] [--provider-segment <provider-segment>]
[--qos-policy <qos-policy>] [--qos-policy <qos-policy>]
[--transparent-vlan | --no-transparent-vlan] [--transparent-vlan | --no-transparent-vlan]
[--tag <tag> | --no-tag]
<name> <name>
.. option:: --project <project> .. option:: --project <project>
@ -165,6 +166,18 @@ Create new network
*Network version 2 only* *Network version 2 only*
.. option:: --tag <tag>
Tag to be added to the network (repeat option to set multiple tags)
*Network version 2 only*
.. option:: --no-tag
No tags associated with the network
*Network version 2 only*
.. _network_create-name: .. _network_create-name:
.. describe:: <name> .. describe:: <name>
@ -206,6 +219,8 @@ List networks
[--provider-physical-network <provider-physical-network>] [--provider-physical-network <provider-physical-network>]
[--provider-segment <provider-segment>] [--provider-segment <provider-segment>]
[--agent <agent-id>] [--agent <agent-id>]
[--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
[--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --external .. option:: --external
@ -297,6 +312,32 @@ List networks
List networks hosted by agent (ID only) List networks hosted by agent (ID only)
*Network version 2 only*
.. option:: --tags <tag>[,<tag>,...]
List networks which have all given tag(s)
*Network version 2 only*
.. option:: --any-tags <tag>[,<tag>,...]
List networks which have any given tag(s)
*Network version 2 only*
.. option:: --not-tags <tag>[,<tag>,...]
Exclude networks which have all given tag(s)
*Network version 2 only*
.. option:: --not-any-tags <tag>[,<tag>,...]
Exclude networks which have any given tag(s)
*Network version 2 only*
network set network set
----------- -----------
@ -318,6 +359,7 @@ Set network properties
[--provider-physical-network <provider-physical-network>] [--provider-physical-network <provider-physical-network>]
[--provider-segment <provider-segment>] [--provider-segment <provider-segment>]
[--qos-policy <qos-policy> | --no-qos-policy] [--qos-policy <qos-policy> | --no-qos-policy]
[--tag <tag>] [--no-tag]
<network> <network>
.. option:: --name <name> .. option:: --name <name>
@ -392,6 +434,15 @@ Set network properties
Remove the QoS policy attached to this network Remove the QoS policy attached to this network
.. option:: --tag <tag>
Tag to be added to the network (repeat option to set multiple tags)
.. option:: --no-tag
Clear tags associated with the network. Specify both --tag
and --no-tag to overwrite current tags
.. _network_set-network: .. _network_set-network:
.. describe:: <network> .. describe:: <network>
@ -412,3 +463,31 @@ Display network details
.. describe:: <network> .. describe:: <network>
Network to display (name or ID) Network to display (name or ID)
network unset
-------------
Unset network properties
*Network version 2 only*
.. program:: network unset
.. code:: bash
openstack network unset
[--tag <tag> | --all-tag]
<network>
.. option:: --tag <tag>
Tag to be removed from the network
(repeat option to remove multiple tags)
.. option:: --all-tag
Clear all tags associated with the network
.. _network_unset-network:
.. describe:: <network>
Network to modify (name or ID)

View File

@ -33,6 +33,7 @@ Create new port
[--qos-policy <qos-policy>] [--qos-policy <qos-policy>]
[--project <project> [--project-domain <project-domain>]] [--project <project> [--project-domain <project-domain>]]
[--enable-port-security | --disable-port-security] [--enable-port-security | --disable-port-security]
[--tag <tag> | --no-tag]
<name> <name>
.. option:: --network <network> .. option:: --network <network>
@ -126,6 +127,14 @@ Create new port
Disable port security for this port Disable port security for this port
.. option:: --tag <tag>
Tag to be added to the port (repeat option to set multiple tags)
.. option:: --no-tag
No tags associated with the port
.. _port_create-name: .. _port_create-name:
.. describe:: <name> .. describe:: <name>
@ -163,6 +172,8 @@ List ports
[--fixed-ip subnet=<subnet>,ip-address=<ip-address>] [--fixed-ip subnet=<subnet>,ip-address=<ip-address>]
[--long] [--long]
[--project <project> [--project-domain <project-domain>]] [--project <project> [--project-domain <project-domain>]]
[--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
[--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --device-owner <device-owner> .. option:: --device-owner <device-owner>
@ -204,6 +215,22 @@ List ports
Domain the project belongs to (name or ID). Domain the project belongs to (name or ID).
This can be used in case collisions between project names exist. This can be used in case collisions between project names exist.
.. option:: --tags <tag>[,<tag>,...]
List ports which have all given tag(s)
.. option:: --any-tags <tag>[,<tag>,...]
List ports which have any given tag(s)
.. option:: --not-tags <tag>[,<tag>,...]
Exclude ports which have all given tag(s)
.. option:: --not-any-tags <tag>[,<tag>,...]
Exclude ports which have any given tag(s)
port set port set
-------- --------
@ -233,6 +260,7 @@ Set port properties
[--allowed-address ip-address=<ip-address>[,mac-address=<mac-address>]] [--allowed-address ip-address=<ip-address>[,mac-address=<mac-address>]]
[--no-allowed-address] [--no-allowed-address]
[--data-plane-status <status>] [--data-plane-status <status>]
[--tag <tag>] [--no-tag]
<port> <port>
.. option:: --description <description> .. option:: --description <description>
@ -342,6 +370,15 @@ Set port properties
Unset it to None with the 'port unset' command Unset it to None with the 'port unset' command
(requires data plane status extension) (requires data plane status extension)
.. option:: --tag <tag>
Tag to be added to the port (repeat option to set multiple tags)
.. option:: --no-tag
Clear tags associated with the port. Specify both --tag
and --no-tag to overwrite current tags
.. _port_set-port: .. _port_set-port:
.. describe:: <port> .. describe:: <port>
@ -378,6 +415,7 @@ Unset port properties
[--allowed-address ip-address=<ip-address>[,mac-address=<mac-address>] [...]] [--allowed-address ip-address=<ip-address>[,mac-address=<mac-address>] [...]]
[--qos-policy] [--qos-policy]
[--data-plane-status] [--data-plane-status]
[--tag <tag> | --all-tag]
<port> <port>
.. option:: --fixed-ip subnet=<subnet>,ip-address=<ip-address> .. option:: --fixed-ip subnet=<subnet>,ip-address=<ip-address>
@ -410,6 +448,15 @@ Unset port properties
Clear existing information of data plane status Clear existing information of data plane status
.. option:: --tag <tag>
Tag to be removed from the port
(repeat option to remove multiple tags)
.. option:: --all-tag
Clear all tags associated with the port
.. _port_unset-port: .. _port_unset-port:
.. describe:: <port> .. describe:: <port>

View File

@ -67,6 +67,7 @@ Create new router
[--ha | --no-ha] [--ha | --no-ha]
[--description <description>] [--description <description>]
[--availability-zone-hint <availability-zone>] [--availability-zone-hint <availability-zone>]
[--tag <tag> | --no-tag]
<name> <name>
.. option:: --project <project> .. option:: --project <project>
@ -121,6 +122,14 @@ Create new router
(Router Availability Zone extension required, (Router Availability Zone extension required,
repeat option to set multiple availability zones) repeat option to set multiple availability zones)
.. option:: --tag <tag>
Tag to be added to the router (repeat option to set multiple tags)
.. option:: --no-tag
No tags associated with the router
.. _router_create-name: .. _router_create-name:
.. describe:: <name> .. describe:: <name>
@ -156,6 +165,8 @@ List routers
[--long] [--long]
[--project <project> [--project-domain <project-domain>]] [--project <project> [--project-domain <project-domain>]]
[--agent <agent-id>] [--agent <agent-id>]
[--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
[--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --agent <agent-id> .. option:: --agent <agent-id>
@ -186,6 +197,22 @@ List routers
Domain the project belongs to (name or ID). Domain the project belongs to (name or ID).
This can be used in case collisions between project names exist. This can be used in case collisions between project names exist.
.. option:: --tags <tag>[,<tag>,...]
List routers which have all given tag(s)
.. option:: --any-tags <tag>[,<tag>,...]
List routers which have any given tag(s)
.. option:: --not-tags <tag>[,<tag>,...]
Exclude routers which have all given tag(s)
.. option:: --not-any-tags <tag>[,<tag>,...]
Exclude routers which have any given tag(s)
router remove port router remove port
------------------ ------------------
@ -246,6 +273,7 @@ Set router properties
[--route destination=<subnet>,gateway=<ip-address> | --no-route] [--route destination=<subnet>,gateway=<ip-address> | --no-route]
[--ha | --no-ha] [--ha | --no-ha]
[--external-gateway <network> [--enable-snat|--disable-snat] [--fixed-ip subnet=<subnet>,ip-address=<ip-address>]] [--external-gateway <network> [--enable-snat|--disable-snat] [--fixed-ip subnet=<subnet>,ip-address=<ip-address>]]
[--tag <tag>] [--no-tag]
<router> <router>
.. option:: --name <name> .. option:: --name <name>
@ -311,6 +339,15 @@ Set router properties
subnet=<subnet>,ip-address=<ip-address> subnet=<subnet>,ip-address=<ip-address>
(repeat option to set multiple fixed IP addresses) (repeat option to set multiple fixed IP addresses)
.. option:: --tag <tag>
Tag to be added to the router (repeat option to set multiple tags)
.. option:: --no-tag
Clear tags associated with the router. Specify both --tag
and --no-tag to overwrite current tags
.. _router_set-router: .. _router_set-router:
.. describe:: <router> .. describe:: <router>
@ -343,6 +380,7 @@ Unset router properties
openstack router unset openstack router unset
[--route destination=<subnet>,gateway=<ip-address>] [--route destination=<subnet>,gateway=<ip-address>]
[--external-gateway] [--external-gateway]
[--tag <tag> | --all-tag]
<router> <router>
.. option:: --route destination=<subnet>,gateway=<ip-address> .. option:: --route destination=<subnet>,gateway=<ip-address>
@ -356,6 +394,15 @@ Unset router properties
Remove external gateway information from the router Remove external gateway information from the router
.. option:: --tag <tag>
Tag to be removed from the router
(repeat option to remove multiple tags)
.. option:: --all-tag
Clear all tags associated with the router
.. _router_unset-router: .. _router_unset-router:
.. describe:: <router> .. describe:: <router>

View File

@ -25,6 +25,7 @@ Create subnet pool
[--default | --no-default] [--default | --no-default]
[--share | --no-share] [--share | --no-share]
[--default-quota <num-ip-addresses>] [--default-quota <num-ip-addresses>]
[--tag <tag> | --no-tag]
--pool-prefix <pool-prefix> [...] --pool-prefix <pool-prefix> [...]
<name> <name>
@ -79,6 +80,14 @@ Create subnet pool
Set default quota for subnet pool as the number of Set default quota for subnet pool as the number of
IP addresses allowed in a subnet IP addresses allowed in a subnet
.. option:: --tag <tag>
Tag to be added to the subnet pool (repeat option to set multiple tags)
.. option:: --no-tag
No tags associated with the subnet pool
.. option:: --pool-prefix <pool-prefix> .. option:: --pool-prefix <pool-prefix>
Set subnet pool prefixes (in CIDR notation) Set subnet pool prefixes (in CIDR notation)
@ -120,6 +129,8 @@ List subnet pools
[--project <project> [--project-domain <project-domain>]] [--project <project> [--project-domain <project-domain>]]
[--name <name>] [--name <name>]
[--address-scope <address-scope>] [--address-scope <address-scope>]
[--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
[--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --long .. option:: --long
@ -158,6 +169,22 @@ List subnet pools
List only subnet pools of given address scope in output (name or ID) List only subnet pools of given address scope in output (name or ID)
.. option:: --tags <tag>[,<tag>,...]
List subnet pools which have all given tag(s)
.. option:: --any-tags <tag>[,<tag>,...]
List subnet pools which have any given tag(s)
.. option:: --not-tags <tag>[,<tag>,...]
Exclude subnet pools which have all given tag(s)
.. option:: --not-any-tags <tag>[,<tag>,...]
Exclude subnet pools which have any given tag(s)
subnet pool set subnet pool set
--------------- ---------------
@ -176,6 +203,7 @@ Set subnet pool properties
[--default | --no-default] [--default | --no-default]
[--description <description>] [--description <description>]
[--default-quota <num-ip-addresses>] [--default-quota <num-ip-addresses>]
[--tag <tag>] [--no-tag]
<subnet-pool> <subnet-pool>
.. option:: --name <name> .. option:: --name <name>
@ -225,6 +253,15 @@ Set subnet pool properties
Set default quota for subnet pool as the number of Set default quota for subnet pool as the number of
IP addresses allowed in a subnet IP addresses allowed in a subnet
.. option:: --tag <tag>
Tag to be added to the subnet pool (repeat option to set multiple tags)
.. option:: --no-tag
Clear tags associated with the subnet pool. Specify both --tag
and --no-tag to overwrite current tags
.. _subnet_pool_set-subnet-pool: .. _subnet_pool_set-subnet-pool:
.. describe:: <subnet-pool> .. describe:: <subnet-pool>
@ -256,6 +293,7 @@ Unset subnet pool properties
openstack subnet pool unset openstack subnet pool unset
[--pool-prefix <pool-prefix> [...]] [--pool-prefix <pool-prefix> [...]]
[--tag <tag> | --all-tag]
<subnet-pool> <subnet-pool>
.. option:: --pool-prefix <pool-prefix> .. option:: --pool-prefix <pool-prefix>
@ -263,6 +301,15 @@ Unset subnet pool properties
Remove subnet pool prefixes (in CIDR notation). Remove subnet pool prefixes (in CIDR notation).
(repeat option to unset multiple prefixes). (repeat option to unset multiple prefixes).
.. option:: --tag <tag>
Tag to be removed from the subnet pool
(repeat option to remove multiple tags)
.. option:: --all-tag
Clear all tags associated with the subnet pool
.. _subnet_pool_unset-subnet-pool: .. _subnet_pool_unset-subnet-pool:
.. describe:: <subnet-pool> .. describe:: <subnet-pool>

View File

@ -31,6 +31,7 @@ Create new subnet
[--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}] [--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
[--network-segment <network-segment>] [--network-segment <network-segment>]
[--service-type <service-type>] [--service-type <service-type>]
[--tag <tag> | --no-tag]
--network <network> --network <network>
<name> <name>
@ -125,6 +126,14 @@ Create new subnet
Must be a valid device owner value for a network port Must be a valid device owner value for a network port
(repeat option to set multiple service types) (repeat option to set multiple service types)
.. option:: --tag <tag>
Tag to be added to the subnet (repeat option to set multiple tags)
.. option:: --no-tag
No tags associated with the subnet
.. option:: --network <network> .. option:: --network <network>
Network this subnet belongs to (name or ID) Network this subnet belongs to (name or ID)
@ -167,6 +176,8 @@ List subnets
[--gateway <gateway>] [--gateway <gateway>]
[--name <name>] [--name <name>]
[--subnet-range <subnet-range>] [--subnet-range <subnet-range>]
[--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
[--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --long .. option:: --long
@ -218,6 +229,22 @@ List subnets
List only subnets of given subnet range (in CIDR notation) in output List only subnets of given subnet range (in CIDR notation) in output
e.g.: ``--subnet-range 10.10.0.0/16`` e.g.: ``--subnet-range 10.10.0.0/16``
.. option:: --tags <tag>[,<tag>,...]
List subnets which have all given tag(s)
.. option:: --any-tags <tag>[,<tag>,...]
List subnets which have any given tag(s)
.. option:: --not-tags <tag>[,<tag>,...]
Exclude subnets which have all given tag(s)
.. option:: --not-any-tags <tag>[,<tag>,...]
Exclude subnets which have any given tag(s)
subnet set subnet set
---------- ----------
@ -238,6 +265,7 @@ Set subnet properties
[--service-type <service-type>] [--service-type <service-type>]
[--name <new-name>] [--name <new-name>]
[--description <description>] [--description <description>]
[--tag <tag>] [--no-tag]
<subnet> <subnet>
.. option:: --allocation-pool start=<ip-address>,end=<ip-address> .. option:: --allocation-pool start=<ip-address>,end=<ip-address>
@ -305,6 +333,15 @@ Set subnet properties
Updated name of the subnet Updated name of the subnet
.. option:: --tag <tag>
Tag to be added to the subnet (repeat option to set multiple tags)
.. option:: --no-tag
Clear tags associated with the subnet. Specify both --tag
and --no-tag to overwrite current tags
.. _subnet_set-subnet: .. _subnet_set-subnet:
.. describe:: <subnet> .. describe:: <subnet>
@ -340,6 +377,7 @@ Unset subnet properties
[--dns-nameserver <dns-nameserver> [...]] [--dns-nameserver <dns-nameserver> [...]]
[--host-route destination=<subnet>,gateway=<ip-address> [...]] [--host-route destination=<subnet>,gateway=<ip-address> [...]]
[--service-type <service-type>] [--service-type <service-type>]
[--tag <tag> | --all-tag]
<subnet> <subnet>
.. option:: --dns-nameserver <dns-nameserver> .. option:: --dns-nameserver <dns-nameserver>
@ -368,6 +406,15 @@ Unset subnet properties
Must be a valid device owner value for a network port Must be a valid device owner value for a network port
(repeat option to unset multiple service types) (repeat option to unset multiple service types)
.. option:: --tag <tag>
Tag to be removed from the subnet
(repeat option to remove multiple tags)
.. option:: --all-tag
Clear all tags associated with the subnet
.. _subnet_unset-subnet: .. _subnet_unset-subnet:
.. describe:: <subnet> .. describe:: <subnet>

View File

@ -0,0 +1,134 @@
# 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 argparse
from openstackclient.i18n import _
class _CommaListAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values.split(','))
def add_tag_filtering_option_to_parser(parser, collection_name):
parser.add_argument(
'--tags',
metavar='<tag>[,<tag>,...]',
action=_CommaListAction,
help=_('List %s which have all given tag(s) '
'(Comma-separated list of tags)') % collection_name
)
parser.add_argument(
'--any-tags',
metavar='<tag>[,<tag>,...]',
action=_CommaListAction,
help=_('List %s which have any given tag(s) '
'(Comma-separated list of tags)') % collection_name
)
parser.add_argument(
'--not-tags',
metavar='<tag>[,<tag>,...]',
action=_CommaListAction,
help=_('Exclude %s which have all given tag(s) '
'(Comma-separated list of tags)') % collection_name
)
parser.add_argument(
'--not-any-tags',
metavar='<tag>[,<tag>,...]',
action=_CommaListAction,
help=_('Exclude %s which have any given tag(s) '
'(Comma-separated list of tags)') % collection_name
)
def get_tag_filtering_args(parsed_args, args):
if parsed_args.tags:
args['tags'] = ','.join(parsed_args.tags)
if parsed_args.any_tags:
args['any_tags'] = ','.join(parsed_args.any_tags)
if parsed_args.not_tags:
args['not_tags'] = ','.join(parsed_args.not_tags)
if parsed_args.not_any_tags:
args['not_any_tags'] = ','.join(parsed_args.not_any_tags)
def add_tag_option_to_parser_for_create(parser, resource_name):
tag_group = parser.add_mutually_exclusive_group()
tag_group.add_argument(
'--tag',
action='append',
dest='tags',
metavar='<tag>',
help=_("Tag to be added to the %s "
"(repeat option to set multiple tags)") % resource_name
)
tag_group.add_argument(
'--no-tag',
action='store_true',
help=_("No tags associated with the %s") % resource_name
)
def add_tag_option_to_parser_for_set(parser, resource_name):
parser.add_argument(
'--tag',
action='append',
dest='tags',
metavar='<tag>',
help=_("Tag to be added to the %s "
"(repeat option to set multiple tags)") % resource_name
)
parser.add_argument(
'--no-tag',
action='store_true',
help=_("Clear tags associated with the %s. Specify both "
"--tag and --no-tag to overwrite current tags") % resource_name
)
def update_tags_for_set(client, obj, parsed_args):
if parsed_args.no_tag:
tags = set()
else:
tags = set(obj.tags)
if parsed_args.tags:
tags |= set(parsed_args.tags)
if set(obj.tags) != tags:
client.set_tags(obj, list(tags))
def add_tag_option_to_parser_for_unset(parser, resource_name):
tag_group = parser.add_mutually_exclusive_group()
tag_group.add_argument(
'--tag',
action='append',
dest='tags',
metavar='<tag>',
help=_("Tag to be removed from the %s "
"(repeat option to remove multiple tags)") % resource_name)
tag_group.add_argument(
'--all-tag',
action='store_true',
help=_("Clear all tags associated with the %s") % resource_name)
def update_tags_for_unset(client, obj, parsed_args):
tags = set(obj.tags)
if parsed_args.all_tag:
tags = set()
if parsed_args.tags:
tags -= set(parsed_args.tags)
if set(obj.tags) != tags:
client.set_tags(obj, list(tags))

View File

@ -20,6 +20,7 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
from openstackclient.network import common from openstackclient.network import common
from openstackclient.network import sdk_utils from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
def _format_admin_state(item): def _format_admin_state(item):
@ -280,6 +281,7 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
help=_("Do not make the network VLAN transparent")) help=_("Do not make the network VLAN transparent"))
_add_additional_network_options(parser) _add_additional_network_options(parser)
_tag.add_tag_option_to_parser_for_create(parser, _('network'))
return parser return parser
def update_parser_compute(self, parser): def update_parser_compute(self, parser):
@ -299,6 +301,8 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
attrs['vlan_transparent'] = False attrs['vlan_transparent'] = False
obj = client.create_network(**attrs) obj = client.create_network(**attrs)
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_columns_network(obj) display_columns, columns = _get_columns_network(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters) data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data) return (display_columns, data)
@ -424,7 +428,9 @@ class ListNetwork(common.NetworkAndComputeLister):
'--agent', '--agent',
metavar='<agent-id>', metavar='<agent-id>',
dest='agent_id', dest='agent_id',
help=_('List networks hosted by agent (ID only)')) help=_('List networks hosted by agent (ID only)')
)
_tag.add_tag_filtering_option_to_parser(parser, _('networks'))
return parser return parser
def take_action_network(self, client, parsed_args): def take_action_network(self, client, parsed_args):
@ -441,6 +447,7 @@ class ListNetwork(common.NetworkAndComputeLister):
'provider_network_type', 'provider_network_type',
'is_router_external', 'is_router_external',
'availability_zones', 'availability_zones',
'tags',
) )
column_headers = ( column_headers = (
'ID', 'ID',
@ -453,6 +460,7 @@ class ListNetwork(common.NetworkAndComputeLister):
'Network Type', 'Network Type',
'Router Type', 'Router Type',
'Availability Zones', 'Availability Zones',
'Tags',
) )
elif parsed_args.agent_id: elif parsed_args.agent_id:
columns = ( columns = (
@ -534,6 +542,8 @@ class ListNetwork(common.NetworkAndComputeLister):
args['provider:segmentation_id'] = parsed_args.segmentation_id args['provider:segmentation_id'] = parsed_args.segmentation_id
args['provider_segmentation_id'] = parsed_args.segmentation_id args['provider_segmentation_id'] = parsed_args.segmentation_id
_tag.get_tag_filtering_args(parsed_args, args)
data = client.networks(**args) data = client.networks(**args)
return (column_headers, return (column_headers,
@ -656,6 +666,7 @@ class SetNetwork(command.Command):
action='store_true', action='store_true',
help=_("Remove the QoS policy attached to this network") help=_("Remove the QoS policy attached to this network")
) )
_tag.add_tag_option_to_parser_for_set(parser, _('network'))
_add_additional_network_options(parser) _add_additional_network_options(parser)
return parser return parser
@ -664,7 +675,11 @@ class SetNetwork(command.Command):
obj = client.find_network(parsed_args.network, ignore_missing=False) obj = client.find_network(parsed_args.network, ignore_missing=False)
attrs = _get_attrs_network(self.app.client_manager, parsed_args) attrs = _get_attrs_network(self.app.client_manager, parsed_args)
client.update_network(obj, **attrs) if attrs:
client.update_network(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_set(client, obj, parsed_args)
class ShowNetwork(common.NetworkAndComputeShowOne): class ShowNetwork(common.NetworkAndComputeShowOne):
@ -689,3 +704,27 @@ class ShowNetwork(common.NetworkAndComputeShowOne):
display_columns, columns = _get_columns_compute(obj) display_columns, columns = _get_columns_compute(obj)
data = utils.get_dict_properties(obj, columns) data = utils.get_dict_properties(obj, columns)
return (display_columns, data) return (display_columns, data)
class UnsetNetwork(command.Command):
_description = _("Unset network properties")
def get_parser(self, prog_name):
parser = super(UnsetNetwork, self).get_parser(prog_name)
parser.add_argument(
'network',
metavar="<network>",
help=_("Network to modify (name or ID)")
)
_tag.add_tag_option_to_parser_for_unset(parser, _('network'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_network(parsed_args.network, ignore_missing=False)
# NOTE: As of now, UnsetNetwork has no attributes which need
# to be updated by update_network().
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_unset(client, obj, parsed_args)

View File

@ -26,6 +26,7 @@ from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -47,6 +48,7 @@ _formatters = {
'extra_dhcp_opts': utils.format_list_of_dicts, 'extra_dhcp_opts': utils.format_list_of_dicts,
'fixed_ips': utils.format_list_of_dicts, 'fixed_ips': utils.format_list_of_dicts,
'security_group_ids': utils.format_list, 'security_group_ids': utils.format_list,
'tags': utils.format_list,
} }
@ -384,6 +386,7 @@ class CreatePort(command.ShowOne):
"ip-address=<ip-address>[,mac-address=<mac-address>] " "ip-address=<ip-address>[,mac-address=<mac-address>] "
"(repeat option to set multiple allowed-address pairs)") "(repeat option to set multiple allowed-address pairs)")
) )
_tag.add_tag_option_to_parser_for_create(parser, _('port'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -416,6 +419,8 @@ class CreatePort(command.ShowOne):
attrs['qos_policy_id'] = client.find_qos_policy( attrs['qos_policy_id'] = client.find_qos_policy(
parsed_args.qos_policy, ignore_missing=False).id parsed_args.qos_policy, ignore_missing=False).id
obj = client.create_port(**attrs) obj = client.create_port(**attrs)
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_columns(obj) display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters) data = utils.get_item_properties(obj, columns, formatters=_formatters)
@ -512,6 +517,7 @@ class ListPort(command.Lister):
"(name or ID): subnet=<subnet>,ip-address=<ip-address> " "(name or ID): subnet=<subnet>,ip-address=<ip-address> "
"(repeat option to set multiple fixed IP addresses)"), "(repeat option to set multiple fixed IP addresses)"),
) )
_tag.add_tag_filtering_option_to_parser(parser, _('ports'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -535,8 +541,8 @@ class ListPort(command.Lister):
filters = {} filters = {}
if parsed_args.long: if parsed_args.long:
columns += ('security_group_ids', 'device_owner',) columns += ('security_group_ids', 'device_owner', 'tags')
column_headers += ('Security Groups', 'Device Owner',) column_headers += ('Security Groups', 'Device Owner', 'Tags')
if parsed_args.device_owner is not None: if parsed_args.device_owner is not None:
filters['device_owner'] = parsed_args.device_owner filters['device_owner'] = parsed_args.device_owner
if parsed_args.router: if parsed_args.router:
@ -566,6 +572,8 @@ class ListPort(command.Lister):
filters['fixed_ips'] = _prepare_filter_fixed_ips( filters['fixed_ips'] = _prepare_filter_fixed_ips(
self.app.client_manager, parsed_args) self.app.client_manager, parsed_args)
_tag.get_tag_filtering_args(parsed_args, filters)
data = network_client.ports(**filters) data = network_client.ports(**filters)
return (column_headers, return (column_headers,
@ -694,6 +702,7 @@ class SetPort(command.Command):
"Unset it to None with the 'port unset' command " "Unset it to None with the 'port unset' command "
"(requires data plane status extension)") "(requires data plane status extension)")
) )
_tag.add_tag_option_to_parser_for_set(parser, _('port'))
return parser return parser
@ -750,7 +759,11 @@ class SetPort(command.Command):
if parsed_args.data_plane_status: if parsed_args.data_plane_status:
attrs['data_plane_status'] = parsed_args.data_plane_status attrs['data_plane_status'] = parsed_args.data_plane_status
client.update_port(obj, **attrs) if attrs:
client.update_port(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_set(client, obj, parsed_args)
class ShowPort(command.ShowOne): class ShowPort(command.ShowOne):
@ -834,6 +847,8 @@ class UnsetPort(command.Command):
help=_("Clear existing information of data plane status") help=_("Clear existing information of data plane status")
) )
_tag.add_tag_option_to_parser_for_unset(parser, _('port'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -889,3 +904,6 @@ class UnsetPort(command.Command):
if attrs: if attrs:
client.update_port(obj, **attrs) client.update_port(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_unset(client, obj, parsed_args)

View File

@ -26,6 +26,7 @@ from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -57,6 +58,7 @@ _formatters = {
'availability_zones': utils.format_list, 'availability_zones': utils.format_list,
'availability_zone_hints': utils.format_list, 'availability_zone_hints': utils.format_list,
'routes': _format_routes, 'routes': _format_routes,
'tags': utils.format_list,
} }
@ -217,6 +219,7 @@ class CreateRouter(command.ShowOne):
"(Router Availability Zone extension required, " "(Router Availability Zone extension required, "
"repeat option to set multiple availability zones)") "repeat option to set multiple availability zones)")
) )
_tag.add_tag_option_to_parser_for_create(parser, _('router'))
return parser return parser
@ -229,6 +232,8 @@ class CreateRouter(command.ShowOne):
if parsed_args.no_ha: if parsed_args.no_ha:
attrs['ha'] = False attrs['ha'] = False
obj = client.create_router(**attrs) obj = client.create_router(**attrs)
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_columns(obj) display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters) data = utils.get_item_properties(obj, columns, formatters=_formatters)
@ -310,6 +315,7 @@ class ListRouter(command.Lister):
metavar='<agent-id>', metavar='<agent-id>',
help=_("List routers hosted by an agent (ID only)") help=_("List routers hosted by an agent (ID only)")
) )
_tag.add_tag_filtering_option_to_parser(parser, _('routers'))
return parser return parser
@ -357,6 +363,8 @@ class ListRouter(command.Lister):
args['tenant_id'] = project_id args['tenant_id'] = project_id
args['project_id'] = project_id args['project_id'] = project_id
_tag.get_tag_filtering_args(parsed_args, args)
if parsed_args.agent is not None: if parsed_args.agent is not None:
agent = client.get_agent(parsed_args.agent) agent = client.get_agent(parsed_args.agent)
data = client.agent_hosted_routers(agent) data = client.agent_hosted_routers(agent)
@ -384,6 +392,8 @@ class ListRouter(command.Lister):
column_headers = column_headers + ( column_headers = column_headers + (
'Availability zones', 'Availability zones',
) )
columns = columns + ('tags',)
column_headers = column_headers + ('Tags',)
return (column_headers, return (column_headers,
(utils.get_item_properties( (utils.get_item_properties(
@ -567,6 +577,7 @@ class SetRouter(command.Command):
action='store_true', action='store_true',
help=_("Disable Source NAT on external gateway") help=_("Disable Source NAT on external gateway")
) )
_tag.add_tag_option_to_parser_for_set(parser, _('router'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -625,7 +636,10 @@ class SetRouter(command.Command):
ips.append(ip_spec) ips.append(ip_spec)
gateway_info['external_fixed_ips'] = ips gateway_info['external_fixed_ips'] = ips
attrs['external_gateway_info'] = gateway_info attrs['external_gateway_info'] = gateway_info
client.update_router(obj, **attrs) if attrs:
client.update_router(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_set(client, obj, parsed_args)
class ShowRouter(command.ShowOne): class ShowRouter(command.ShowOne):
@ -675,6 +689,7 @@ class UnsetRouter(command.Command):
metavar="<router>", metavar="<router>",
help=_("Router to modify (name or ID)") help=_("Router to modify (name or ID)")
) )
_tag.add_tag_option_to_parser_for_unset(parser, _('router'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -695,3 +710,5 @@ class UnsetRouter(command.Command):
attrs['external_gateway_info'] = {} attrs['external_gateway_info'] = {}
if attrs: if attrs:
client.update_router(obj, **attrs) client.update_router(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_unset(client, obj, parsed_args)

View File

@ -24,6 +24,7 @@ from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -55,6 +56,7 @@ _formatters = {
'dns_nameservers': utils.format_list, 'dns_nameservers': utils.format_list,
'host_routes': _format_host_routes, 'host_routes': _format_host_routes,
'service_types': utils.format_list, 'service_types': utils.format_list,
'tags': utils.format_list,
} }
@ -336,12 +338,15 @@ class CreateSubnet(command.ShowOne):
help=_("Set subnet description") help=_("Set subnet description")
) )
_get_common_parse_arguments(parser) _get_common_parse_arguments(parser)
_tag.add_tag_option_to_parser_for_create(parser, _('subnet'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
client = self.app.client_manager.network client = self.app.client_manager.network
attrs = _get_attrs(self.app.client_manager, parsed_args) attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_subnet(**attrs) obj = client.create_subnet(**attrs)
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_columns(obj) display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters) data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data) return (display_columns, data)
@ -454,6 +459,7 @@ class ListSubnet(command.Lister):
"(in CIDR notation) in output " "(in CIDR notation) in output "
"e.g.: --subnet-range 10.10.0.0/16") "e.g.: --subnet-range 10.10.0.0/16")
) )
_tag.add_tag_filtering_option_to_parser(parser, _('subnets'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -488,6 +494,7 @@ class ListSubnet(command.Lister):
filters['name'] = parsed_args.name filters['name'] = parsed_args.name
if parsed_args.subnet_range: if parsed_args.subnet_range:
filters['cidr'] = parsed_args.subnet_range filters['cidr'] = parsed_args.subnet_range
_tag.get_tag_filtering_args(parsed_args, filters)
data = network_client.subnets(**filters) data = network_client.subnets(**filters)
headers = ('ID', 'Name', 'Network', 'Subnet') headers = ('ID', 'Name', 'Network', 'Subnet')
@ -495,10 +502,10 @@ class ListSubnet(command.Lister):
if parsed_args.long: if parsed_args.long:
headers += ('Project', 'DHCP', 'Name Servers', headers += ('Project', 'DHCP', 'Name Servers',
'Allocation Pools', 'Host Routes', 'IP Version', 'Allocation Pools', 'Host Routes', 'IP Version',
'Gateway', 'Service Types') 'Gateway', 'Service Types', 'Tags')
columns += ('project_id', 'is_dhcp_enabled', 'dns_nameservers', columns += ('project_id', 'is_dhcp_enabled', 'dns_nameservers',
'allocation_pools', 'host_routes', 'ip_version', 'allocation_pools', 'host_routes', 'ip_version',
'gateway_ip', 'service_types') 'gateway_ip', 'service_types', 'tags')
return (headers, return (headers,
(utils.get_item_properties( (utils.get_item_properties(
@ -549,6 +556,7 @@ class SetSubnet(command.Command):
metavar='<description>', metavar='<description>',
help=_("Set subnet description") help=_("Set subnet description")
) )
_tag.add_tag_option_to_parser_for_set(parser, _('subnet'))
_get_common_parse_arguments(parser, is_create=False) _get_common_parse_arguments(parser, is_create=False)
return parser return parser
@ -574,7 +582,10 @@ class SetSubnet(command.Command):
attrs['allocation_pools'] = [] attrs['allocation_pools'] = []
if 'service_types' in attrs: if 'service_types' in attrs:
attrs['service_types'] += obj.service_types attrs['service_types'] += obj.service_types
client.update_subnet(obj, **attrs) if attrs:
client.update_subnet(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_set(client, obj, parsed_args)
return return
@ -643,6 +654,7 @@ class UnsetSubnet(command.Command):
'Must be a valid device owner value for a network port ' 'Must be a valid device owner value for a network port '
'(repeat option to unset multiple service types)') '(repeat option to unset multiple service types)')
) )
_tag.add_tag_option_to_parser_for_unset(parser, _('subnet'))
parser.add_argument( parser.add_argument(
'subnet', 'subnet',
metavar="<subnet>", metavar="<subnet>",
@ -678,3 +690,6 @@ class UnsetSubnet(command.Command):
attrs['service_types'] = tmp_obj.service_types attrs['service_types'] = tmp_obj.service_types
if attrs: if attrs:
client.update_subnet(obj, **attrs) client.update_subnet(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_unset(client, obj, parsed_args)

View File

@ -24,6 +24,7 @@ from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -42,6 +43,7 @@ def _get_columns(item):
_formatters = { _formatters = {
'prefixes': utils.format_list, 'prefixes': utils.format_list,
'tags': utils.format_list,
} }
@ -191,6 +193,7 @@ class CreateSubnetPool(command.ShowOne):
metavar='<num-ip-addresses>', metavar='<num-ip-addresses>',
help=_("Set default quota for subnet pool as the number of" help=_("Set default quota for subnet pool as the number of"
"IP addresses allowed in a subnet")), "IP addresses allowed in a subnet")),
_tag.add_tag_option_to_parser_for_create(parser, _('subnet pool'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -200,6 +203,8 @@ class CreateSubnetPool(command.ShowOne):
if "prefixes" not in attrs: if "prefixes" not in attrs:
attrs['prefixes'] = [] attrs['prefixes'] = []
obj = client.create_subnet_pool(**attrs) obj = client.create_subnet_pool(**attrs)
# tags cannot be set when created, so tags need to be set later.
_tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_columns(obj) display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters) data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data) return (display_columns, data)
@ -293,6 +298,7 @@ class ListSubnetPool(command.Lister):
help=_("List only subnet pools of given address scope " help=_("List only subnet pools of given address scope "
"in output (name or ID)") "in output (name or ID)")
) )
_tag.add_tag_filtering_option_to_parser(parser, _('subnet pools'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -324,15 +330,16 @@ class ListSubnetPool(command.Lister):
parsed_args.address_scope, parsed_args.address_scope,
ignore_missing=False) ignore_missing=False)
filters['address_scope_id'] = address_scope.id filters['address_scope_id'] = address_scope.id
_tag.get_tag_filtering_args(parsed_args, filters)
data = network_client.subnet_pools(**filters) data = network_client.subnet_pools(**filters)
headers = ('ID', 'Name', 'Prefixes') headers = ('ID', 'Name', 'Prefixes')
columns = ('id', 'name', 'prefixes') columns = ('id', 'name', 'prefixes')
if parsed_args.long: if parsed_args.long:
headers += ('Default Prefix Length', 'Address Scope', headers += ('Default Prefix Length', 'Address Scope',
'Default Subnet Pool', 'Shared') 'Default Subnet Pool', 'Shared', 'Tags')
columns += ('default_prefix_length', 'address_scope_id', columns += ('default_prefix_length', 'address_scope_id',
'is_default', 'is_shared') 'is_default', 'is_shared', 'tags')
return (headers, return (headers,
(utils.get_item_properties( (utils.get_item_properties(
@ -384,6 +391,8 @@ class SetSubnetPool(command.Command):
metavar='<num-ip-addresses>', metavar='<num-ip-addresses>',
help=_("Set default quota for subnet pool as the number of" help=_("Set default quota for subnet pool as the number of"
"IP addresses allowed in a subnet")), "IP addresses allowed in a subnet")),
_tag.add_tag_option_to_parser_for_set(parser, _('subnet pool'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -397,7 +406,10 @@ class SetSubnetPool(command.Command):
if 'prefixes' in attrs: if 'prefixes' in attrs:
attrs['prefixes'].extend(obj.prefixes) attrs['prefixes'].extend(obj.prefixes)
client.update_subnet_pool(obj, **attrs) if attrs:
client.update_subnet_pool(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_set(client, obj, parsed_args)
class ShowSubnetPool(command.ShowOne): class ShowSubnetPool(command.ShowOne):
@ -441,6 +453,7 @@ class UnsetSubnetPool(command.Command):
metavar="<subnet-pool>", metavar="<subnet-pool>",
help=_("Subnet pool to modify (name or ID)") help=_("Subnet pool to modify (name or ID)")
) )
_tag.add_tag_option_to_parser_for_unset(parser, _('subnet pool'))
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -461,3 +474,5 @@ class UnsetSubnetPool(command.Command):
attrs['prefixes'] = tmp_prefixes attrs['prefixes'] = tmp_prefixes
if attrs: if attrs:
client.update_subnet_pool(obj, **attrs) client.update_subnet_pool(obj, **attrs)
# tags is a subresource and it needs to be updated separately.
_tag.update_tags_for_unset(client, obj, parsed_args)

View File

@ -10,6 +10,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import json
import uuid
from openstackclient.tests.functional import base from openstackclient.tests.functional import base
@ -20,3 +23,81 @@ class NetworkTests(base.TestCase):
def setUpClass(cls): def setUpClass(cls):
super(NetworkTests, cls).setUpClass() super(NetworkTests, cls).setUpClass()
cls.haz_network = base.is_service_enabled('network') cls.haz_network = base.is_service_enabled('network')
class NetworkTagTests(NetworkTests):
"""Functional tests with tag operation"""
base_command = None
def test_tag_operation(self):
# Get project IDs
cmd_output = json.loads(self.openstack('token issue -f json '))
auth_project_id = cmd_output['project_id']
# Network create with no options
name1 = self._create_resource_and_tag_check('', [])
# Network create with tags
name2 = self._create_resource_and_tag_check('--tag red --tag blue',
['red', 'blue'])
# Network create with no tag explicitly
name3 = self._create_resource_and_tag_check('--no-tag', [])
self._set_resource_and_tag_check('set', name1, '--tag red --tag green',
['red', 'green'])
list_expected = ((name1, ['red', 'green']),
(name2, ['red', 'blue']),
(name3, []))
self._list_tag_check(auth_project_id, list_expected)
self._set_resource_and_tag_check('set', name1, '--tag blue',
['red', 'green', 'blue'])
self._set_resource_and_tag_check(
'set', name1,
'--no-tag --tag yellow --tag orange --tag purple',
['yellow', 'orange', 'purple'])
self._set_resource_and_tag_check('unset', name1, '--tag yellow',
['orange', 'purple'])
self._set_resource_and_tag_check('unset', name1, '--all-tag', [])
self._set_resource_and_tag_check('set', name2, '--no-tag', [])
def _assertTagsEqual(self, expected, actual):
# TODO(amotoki): Should migrate to cliff format columns.
# At now, unit test assert method needs to be replaced
# to handle format columns, so format_list() is used.
# NOTE: The order of tag is undeterminestic.
actual_tags = filter(bool, actual.split(', '))
self.assertEqual(set(expected), set(actual_tags))
def _list_tag_check(self, project_id, expected):
cmd_output = json.loads(self.openstack(
'{} list --long --project {} -f json'.format(self.base_command,
project_id)))
for name, tags in expected:
net = [n for n in cmd_output if n['Name'] == name][0]
self._assertTagsEqual(tags, net['Tags'])
def _create_resource_for_tag_test(self, name, args):
return json.loads(self.openstack(
'{} create -f json {} {}'.format(self.base_command, args, name)
))
def _create_resource_and_tag_check(self, args, expected):
name = uuid.uuid4().hex
cmd_output = self._create_resource_for_tag_test(name, args)
self.addCleanup(
self.openstack, '{} delete {}'.format(self.base_command, name))
self.assertIsNotNone(cmd_output["id"])
self._assertTagsEqual(expected, cmd_output['tags'])
return name
def _set_resource_and_tag_check(self, command, name, args, expected):
cmd_output = self.openstack(
'{} {} {} {}'.format(self.base_command, command, args, name)
)
self.assertFalse(cmd_output)
cmd_output = json.loads(self.openstack(
'{} show -f json {}'.format(self.base_command, name)
))
self._assertTagsEqual(expected, cmd_output['tags'])

View File

@ -16,9 +16,11 @@ import uuid
from openstackclient.tests.functional.network.v2 import common from openstackclient.tests.functional.network.v2 import common
class NetworkTests(common.NetworkTests): class NetworkTests(common.NetworkTagTests):
"""Functional tests for network""" """Functional tests for network"""
base_command = 'network'
def setUp(self): def setUp(self):
super(NetworkTests, self).setUp() super(NetworkTests, self).setUp()
# Nothing in this class works with Nova Network # Nothing in this class works with Nova Network

View File

@ -16,9 +16,14 @@ import uuid
from openstackclient.tests.functional.network.v2 import common from openstackclient.tests.functional.network.v2 import common
class PortTests(common.NetworkTests): class PortTests(common.NetworkTagTests):
"""Functional tests for port""" """Functional tests for port"""
base_command = 'port'
NAME = uuid.uuid4().hex
NETWORK_NAME = uuid.uuid4().hex
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
common.NetworkTests.setUpClass() common.NetworkTests.setUpClass()
@ -250,3 +255,9 @@ class PortTests(common.NetworkTests):
sg_id2, sg_id2,
json_output.get('security_group_ids'), json_output.get('security_group_ids'),
) )
def _create_resource_for_tag_test(self, name, args):
return json.loads(self.openstack(
'{} create -f json --network {} {} {}'
.format(self.base_command, self.NETWORK_NAME, args, name)
))

View File

@ -16,9 +16,11 @@ import uuid
from openstackclient.tests.functional.network.v2 import common from openstackclient.tests.functional.network.v2 import common
class RouterTests(common.NetworkTests): class RouterTests(common.NetworkTagTests):
"""Functional tests for router""" """Functional tests for router"""
base_command = 'router'
def setUp(self): def setUp(self):
super(RouterTests, self).setUp() super(RouterTests, self).setUp()
# Nothing in this class works with Nova Network # Nothing in this class works with Nova Network

View File

@ -17,9 +17,11 @@ import uuid
from openstackclient.tests.functional.network.v2 import common from openstackclient.tests.functional.network.v2 import common
class SubnetTests(common.NetworkTests): class SubnetTests(common.NetworkTagTests):
"""Functional tests for subnet""" """Functional tests for subnet"""
base_command = 'subnet'
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
common.NetworkTests.setUpClass() common.NetworkTests.setUpClass()
@ -285,3 +287,9 @@ class SubnetTests(common.NetworkTests):
# break and no longer retry if create successfully # break and no longer retry if create successfully
break break
return cmd_output return cmd_output
def _create_resource_for_tag_test(self, name, args):
cmd = ('subnet create -f json --network ' +
self.NETWORK_NAME + ' ' + args +
' --subnet-range')
return self._subnet_create(cmd, name)

View File

@ -17,9 +17,11 @@ import uuid
from openstackclient.tests.functional.network.v2 import common from openstackclient.tests.functional.network.v2 import common
class SubnetPoolTests(common.NetworkTests): class SubnetPoolTests(common.NetworkTagTests):
"""Functional tests for subnet pool""" """Functional tests for subnet pool"""
base_command = 'subnet pool'
def setUp(self): def setUp(self):
super(SubnetPoolTests, self).setUp() super(SubnetPoolTests, self).setUp()
# Nothing in this class works with Nova Network # Nothing in this class works with Nova Network
@ -321,3 +323,7 @@ class SubnetPoolTests(common.NetworkTests):
break break
return cmd_output, pool_prefix return cmd_output, pool_prefix
def _create_resource_for_tag_test(self, name, args):
cmd_output, _pool_prefix = self._subnet_pool_create(args, name)
return cmd_output

View File

@ -0,0 +1,190 @@
# 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.unit import utils as tests_utils
class TestCreateTagMixin(object):
"""Test case mixin to test network tag operation for resource creation.
* Each test class must create a mock for self.network.set_tags
* If you test tag operation combined with other options,
you need to write test(s) directly in individual test cases.
* The following instance attributes must be defined:
* _tag_test_resource: Test resource returned by mocked create_<resource>.
* _tag_create_resource_mock: Mocked create_<resource> method of SDK.
* _tag_create_required_arglist: List of required arguments when creating
a resource with default options.
* _tag_create_required_verifylist: List of expected parsed_args params
when creating a resource with default options.
* _tag_create_required_attrs: Expected attributes passed to a mocked
create_resource method when creating a resource with default options.
"""
def _test_create_with_tag(self, add_tags=True):
arglist = self._tag_create_required_arglist[:]
if add_tags:
arglist += ['--tag', 'red', '--tag', 'blue']
else:
arglist += ['--no-tag']
verifylist = self._tag_create_required_verifylist[:]
if add_tags:
verifylist.append(('tags', ['red', 'blue']))
else:
verifylist.append(('no_tag', True))
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
self._tag_create_resource_mock.assert_called_once_with(
**self._tag_create_required_attrs)
if add_tags:
self.network.set_tags.assert_called_once_with(
self._tag_test_resource,
tests_utils.CompareBySet(['red', 'blue']))
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
def test_create_with_no_tag(self):
self._test_create_with_tag(add_tags=False)
class TestListTagMixin(object):
"""Test case mixin to test network tag operation for resource listing.
* A test resource returned by find_<resource> must contains
"red" and "green" tags.
* Each test class must create a mock for self.network.set_tags
* If you test tag operation combined with other options,
you need to write test(s) directly in individual test cases.
* The following instance attributes must be defined:
* _tag_create_resource_mock: Mocked list_<resource> method of SDK.
"""
def test_list_with_tag_options(self):
arglist = [
'--tags', 'red,blue',
'--any-tags', 'red,green',
'--not-tags', 'orange,yellow',
'--not-any-tags', 'black,white',
]
verifylist = [
('tags', ['red', 'blue']),
('any_tags', ['red', 'green']),
('not_tags', ['orange', 'yellow']),
('not_any_tags', ['black', 'white']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self._tag_list_resource_mock.assert_called_once_with(
**{'tags': 'red,blue',
'any_tags': 'red,green',
'not_tags': 'orange,yellow',
'not_any_tags': 'black,white'}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
class TestSetTagMixin(object):
"""Test case mixin to test network tag operation for resource update.
* A test resource returned by find_<resource> must contains
"red" and "green" tags.
* Each test class must create a mock for self.network.set_tags
* If you test tag operation combined with other options,
you need to write test(s) directly in individual test cases.
* The following instance attributes must be defined:
* _tag_resource_name: positional arg name of a resource to be updated.
* _tag_test_resource: Test resource returned by mocked update_<resource>.
* _tag_update_resource_mock: Mocked update_<resource> method of SDK.
"""
def _test_set_tags(self, with_tags=True):
if with_tags:
arglist = ['--tag', 'red', '--tag', 'blue']
verifylist = [('tags', ['red', 'blue'])]
expected_args = ['red', 'blue', 'green']
else:
arglist = ['--no-tag']
verifylist = [('no_tag', True)]
expected_args = []
arglist.append(self._tag_test_resource.name)
verifylist.append(
(self._tag_resource_name, self._tag_test_resource.name))
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertFalse(self._tag_update_resource_mock.called)
self.network.set_tags.assert_called_once_with(
self._tag_test_resource,
tests_utils.CompareBySet(expected_args))
self.assertIsNone(result)
def test_set_with_tags(self):
self._test_set_tags(with_tags=True)
def test_set_with_no_tag(self):
self._test_set_tags(with_tags=False)
class TestUnsetTagMixin(object):
"""Test case mixin to test network tag operation for resource update.
* Each test class must create a mock for self.network.set_tags
* If you test tag operation combined with other options,
you need to write test(s) directly in individual test cases.
* The following instance attributes must be defined:
* _tag_resource_name: positional arg name of a resource to be updated.
* _tag_test_resource: Test resource returned by mocked update_<resource>.
* _tag_update_resource_mock: Mocked update_<resource> method of SDK.
"""
def _test_unset_tags(self, with_tags=True):
if with_tags:
arglist = ['--tag', 'red', '--tag', 'blue']
verifylist = [('tags', ['red', 'blue'])]
expected_args = ['green']
else:
arglist = ['--all-tag']
verifylist = [('all_tag', True)]
expected_args = []
arglist.append(self._tag_test_resource.name)
verifylist.append(
(self._tag_resource_name, self._tag_test_resource.name))
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertFalse(self._tag_update_resource_mock.called)
self.network.set_tags.assert_called_once_with(
self._tag_test_resource,
tests_utils.CompareBySet(expected_args))
self.assertIsNone(result)
def test_unset_with_tags(self):
self._test_unset_tags(with_tags=True)
def test_unset_with_all_tag(self):
self._test_unset_tags(with_tags=False)

View File

@ -350,6 +350,7 @@ class FakeNetwork(object):
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex, 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex, 'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
'tags': [],
} }
# Overwrite default attributes. # Overwrite default attributes.
@ -576,6 +577,7 @@ class FakePort(object):
'status': 'ACTIVE', 'status': 'ACTIVE',
'tenant_id': 'project-id-' + uuid.uuid4().hex, 'tenant_id': 'project-id-' + uuid.uuid4().hex,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'tags': [],
} }
# Overwrite default attributes. # Overwrite default attributes.
@ -1053,6 +1055,7 @@ class FakeRouter(object):
'external_gateway_info': {}, 'external_gateway_info': {},
'availability_zone_hints': [], 'availability_zone_hints': [],
'availability_zones': [], 'availability_zones': [],
'tags': [],
} }
# Overwrite default attributes. # Overwrite default attributes.
@ -1294,6 +1297,7 @@ class FakeSubnet(object):
'service_types': [], 'service_types': [],
'subnetpool_id': None, 'subnetpool_id': None,
'description': 'subnet-description-' + uuid.uuid4().hex, 'description': 'subnet-description-' + uuid.uuid4().hex,
'tags': [],
} }
# Overwrite default attributes. # Overwrite default attributes.
@ -1544,6 +1548,7 @@ class FakeSubnetPool(object):
'default_quota': None, 'default_quota': None,
'ip_version': '4', 'ip_version': '4',
'description': 'subnet-pool-description-' + uuid.uuid4().hex, 'description': 'subnet-pool-description-' + uuid.uuid4().hex,
'tags': [],
} }
# Overwrite default attributes. # Overwrite default attributes.

View File

@ -22,6 +22,7 @@ from openstackclient.network.v2 import network
from openstackclient.tests.unit import fakes from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2 from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import _test_tag
from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit import utils as tests_utils
@ -41,7 +42,7 @@ class TestNetwork(network_fakes.TestNetworkV2):
self.domains_mock = self.app.client_manager.identity.domains self.domains_mock = self.app.client_manager.identity.domains
class TestCreateNetworkIdentityV3(TestNetwork): class TestCreateNetworkIdentityV3(TestNetwork, _test_tag.TestCreateTagMixin):
project = identity_fakes_v3.FakeProject.create_one_project() project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain() domain = identity_fakes_v3.FakeDomain.create_one_domain()
@ -105,6 +106,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
super(TestCreateNetworkIdentityV3, self).setUp() super(TestCreateNetworkIdentityV3, self).setUp()
self.network.create_network = mock.Mock(return_value=self._network) self.network.create_network = mock.Mock(return_value=self._network)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = network.CreateNetwork(self.app, self.namespace) self.cmd = network.CreateNetwork(self.app, self.namespace)
@ -113,6 +115,22 @@ class TestCreateNetworkIdentityV3(TestNetwork):
self.domains_mock.get.return_value = self.domain self.domains_mock.get.return_value = self.domain
self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy) self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy)
# TestCreateTagMixin
self._tag_test_resource = self._network
self._tag_create_resource_mock = self.network.create_network
self._tag_create_required_arglist = [self._network.name]
self._tag_create_required_verifylist = [
('name', self._network.name),
('enable', True),
('share', None),
('project', None),
('external', False),
]
self._tag_create_required_attrs = {
'admin_state_up': True,
'name': self._network.name,
}
def test_create_no_options(self): def test_create_no_options(self):
arglist = [] arglist = []
verifylist = [] verifylist = []
@ -139,6 +157,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'admin_state_up': True, 'admin_state_up': True,
'name': self._network.name, 'name': self._network.name,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
@ -287,6 +306,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
super(TestCreateNetworkIdentityV2, self).setUp() super(TestCreateNetworkIdentityV2, self).setUp()
self.network.create_network = mock.Mock(return_value=self._network) self.network.create_network = mock.Mock(return_value=self._network)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = network.CreateNetwork(self.app, self.namespace) self.cmd = network.CreateNetwork(self.app, self.namespace)
@ -328,6 +348,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'tenant_id': self.project.id, 'tenant_id': self.project.id,
'project_id': self.project.id, 'project_id': self.project.id,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
@ -440,7 +461,7 @@ class TestDeleteNetwork(TestNetwork):
self.network.delete_network.assert_has_calls(calls) self.network.delete_network.assert_has_calls(calls)
class TestListNetwork(TestNetwork): class TestListNetwork(TestNetwork, _test_tag.TestListTagMixin):
# The networks going to be listed up. # The networks going to be listed up.
_network = network_fakes.FakeNetwork.create_networks(count=3) _network = network_fakes.FakeNetwork.create_networks(count=3)
@ -461,6 +482,7 @@ class TestListNetwork(TestNetwork):
'Network Type', 'Network Type',
'Router Type', 'Router Type',
'Availability Zones', 'Availability Zones',
'Tags',
) )
data = [] data = []
@ -484,6 +506,7 @@ class TestListNetwork(TestNetwork):
net.provider_network_type, net.provider_network_type,
network._format_router_external(net.is_router_external), network._format_router_external(net.is_router_external),
utils.format_list(net.availability_zones), utils.format_list(net.availability_zones),
utils.format_list(net.tags),
)) ))
def setUp(self): def setUp(self):
@ -501,6 +524,9 @@ class TestListNetwork(TestNetwork):
self.network.dhcp_agent_hosting_networks = mock.Mock( self.network.dhcp_agent_hosting_networks = mock.Mock(
return_value=self._network) return_value=self._network)
# TestListTagMixin
self._tag_list_resource_mock = self.network.networks
def test_network_list_no_options(self): def test_network_list_no_options(self):
arglist = [] arglist = []
verifylist = [ verifylist = [
@ -795,10 +821,11 @@ class TestListNetwork(TestNetwork):
self.assertEqual(list(data), list(self.data)) self.assertEqual(list(data), list(self.data))
class TestSetNetwork(TestNetwork): class TestSetNetwork(TestNetwork, _test_tag.TestSetTagMixin):
# The network to set. # The network to set.
_network = network_fakes.FakeNetwork.create_one_network() _network = network_fakes.FakeNetwork.create_one_network(
{'tags': ['green', 'red']})
qos_policy = (network_fakes.FakeNetworkQosPolicy. qos_policy = (network_fakes.FakeNetworkQosPolicy.
create_one_qos_policy(attrs={'id': _network.qos_policy_id})) create_one_qos_policy(attrs={'id': _network.qos_policy_id}))
@ -806,6 +833,7 @@ class TestSetNetwork(TestNetwork):
super(TestSetNetwork, self).setUp() super(TestSetNetwork, self).setUp()
self.network.update_network = mock.Mock(return_value=None) self.network.update_network = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_network = mock.Mock(return_value=self._network) self.network.find_network = mock.Mock(return_value=self._network)
self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy) self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy)
@ -813,6 +841,11 @@ class TestSetNetwork(TestNetwork):
# Get the command object to test # Get the command object to test
self.cmd = network.SetNetwork(self.app, self.namespace) self.cmd = network.SetNetwork(self.app, self.namespace)
# TestSetTagMixin
self._tag_resource_name = 'network'
self._tag_test_resource = self._network
self._tag_update_resource_mock = self.network.update_network
def test_set_this(self): def test_set_this(self):
arglist = [ arglist = [
self._network.name, self._network.name,
@ -902,9 +935,8 @@ class TestSetNetwork(TestNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {} self.assertFalse(self.network.update_network.called)
self.network.update_network.assert_called_once_with( self.assertFalse(self.network.set_tags.called)
self._network, **attrs)
self.assertIsNone(result) self.assertIsNone(result)
@ -990,3 +1022,40 @@ class TestShowNetwork(TestNetwork):
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
class TestUnsetNetwork(TestNetwork, _test_tag.TestUnsetTagMixin):
# The network to set.
_network = network_fakes.FakeNetwork.create_one_network(
{'tags': ['green', 'red']})
qos_policy = (network_fakes.FakeNetworkQosPolicy.
create_one_qos_policy(attrs={'id': _network.qos_policy_id}))
def setUp(self):
super(TestUnsetNetwork, self).setUp()
self.network.update_network = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_network = mock.Mock(return_value=self._network)
self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy)
# Get the command object to test
self.cmd = network.UnsetNetwork(self.app, self.namespace)
# TestUnsetNetwork
self._tag_resource_name = 'network'
self._tag_test_resource = self._network
self._tag_update_resource_mock = self.network.update_network
def test_unset_nothing(self):
arglist = [self._network.name, ]
verifylist = [('network', self._network.name), ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertFalse(self.network.update_network.called)
self.assertFalse(self.network.set_tags.called)
self.assertIsNone(result)

View File

@ -21,6 +21,7 @@ from osc_lib import utils
from openstackclient.network.v2 import port from openstackclient.network.v2 import port
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit.network.v2 import _test_tag
from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit import utils as tests_utils
@ -35,7 +36,8 @@ class TestPort(network_fakes.TestNetworkV2):
# Get a shortcut to the ProjectManager Mock # Get a shortcut to the ProjectManager Mock
self.projects_mock = self.app.client_manager.identity.projects self.projects_mock = self.app.client_manager.identity.projects
def _get_common_cols_data(self, fake_port): @staticmethod
def _get_common_cols_data(fake_port):
columns = ( columns = (
'admin_state_up', 'admin_state_up',
'allowed_address_pairs', 'allowed_address_pairs',
@ -61,6 +63,7 @@ class TestPort(network_fakes.TestNetworkV2):
'qos_policy_id', 'qos_policy_id',
'security_group_ids', 'security_group_ids',
'status', 'status',
'tags',
) )
data = ( data = (
@ -88,19 +91,22 @@ class TestPort(network_fakes.TestNetworkV2):
fake_port.qos_policy_id, fake_port.qos_policy_id,
utils.format_list(fake_port.security_group_ids), utils.format_list(fake_port.security_group_ids),
fake_port.status, fake_port.status,
utils.format_list(fake_port.tags),
) )
return columns, data return columns, data
class TestCreatePort(TestPort): class TestCreatePort(TestPort, _test_tag.TestCreateTagMixin):
_port = network_fakes.FakePort.create_one_port() _port = network_fakes.FakePort.create_one_port()
columns, data = TestPort._get_common_cols_data(_port)
def setUp(self): def setUp(self):
super(TestCreatePort, self).setUp() super(TestCreatePort, self).setUp()
self.network.create_port = mock.Mock(return_value=self._port) self.network.create_port = mock.Mock(return_value=self._port)
self.network.set_tags = mock.Mock(return_value=None)
fake_net = network_fakes.FakeNetwork.create_one_network({ fake_net = network_fakes.FakeNetwork.create_one_network({
'id': self._port.network_id, 'id': self._port.network_id,
}) })
@ -110,6 +116,24 @@ class TestCreatePort(TestPort):
# Get the command object to test # Get the command object to test
self.cmd = port.CreatePort(self.app, self.namespace) self.cmd = port.CreatePort(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_test_resource = self._port
self._tag_create_resource_mock = self.network.create_port
self._tag_create_required_arglist = [
'--network', self._port.network_id,
'test-port',
]
self._tag_create_required_verifylist = [
('network', self._port.network_id,),
('enable', True),
('name', 'test-port'),
]
self._tag_create_required_attrs = {
'admin_state_up': True,
'network_id': self._port.network_id,
'name': 'test-port',
}
def test_create_default_options(self): def test_create_default_options(self):
arglist = [ arglist = [
'--network', self._port.network_id, '--network', self._port.network_id,
@ -129,10 +153,10 @@ class TestCreatePort(TestPort):
'network_id': self._port.network_id, 'network_id': self._port.network_id,
'name': 'test-port', 'name': 'test-port',
}) })
self.assertFalse(self.network.set_tags.called)
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_full_options(self): def test_create_full_options(self):
arglist = [ arglist = [
@ -166,7 +190,6 @@ class TestCreatePort(TestPort):
('network', self._port.network_id), ('network', self._port.network_id),
('dns_name', '8.8.8.8'), ('dns_name', '8.8.8.8'),
('name', 'test-port'), ('name', 'test-port'),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -187,9 +210,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_invalid_json_binding_profile(self): def test_create_invalid_json_binding_profile(self):
arglist = [ arglist = [
@ -239,9 +261,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_with_security_group(self): def test_create_with_security_group(self):
secgroup = network_fakes.FakeSecurityGroup.create_one_security_group() secgroup = network_fakes.FakeSecurityGroup.create_one_security_group()
@ -269,9 +290,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_port_with_dns_name(self): def test_create_port_with_dns_name(self):
arglist = [ arglist = [
@ -296,9 +316,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_with_security_groups(self): def test_create_with_security_groups(self):
sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group()
@ -327,9 +346,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_with_no_security_groups(self): def test_create_with_no_security_groups(self):
arglist = [ arglist = [
@ -354,9 +372,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_port_with_allowed_address_pair_ipaddr(self): def test_create_port_with_allowed_address_pair_ipaddr(self):
pairs = [{'ip_address': '192.168.1.123'}, pairs = [{'ip_address': '192.168.1.123'},
@ -385,9 +402,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_port_with_allowed_address_pair(self): def test_create_port_with_allowed_address_pair(self):
pairs = [{'ip_address': '192.168.1.123', pairs = [{'ip_address': '192.168.1.123',
@ -422,9 +438,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_port_with_qos(self): def test_create_port_with_qos(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
@ -451,9 +466,8 @@ class TestCreatePort(TestPort):
'name': 'test-port', 'name': 'test-port',
}) })
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
def test_create_port_security_enabled(self): def test_create_port_security_enabled(self):
arglist = [ arglist = [
@ -583,7 +597,7 @@ class TestDeletePort(TestPort):
) )
class TestListPort(TestPort): class TestListPort(TestPort, _test_tag.TestListTagMixin):
_ports = network_fakes.FakePort.create_ports(count=3) _ports = network_fakes.FakePort.create_ports(count=3)
@ -603,6 +617,7 @@ class TestListPort(TestPort):
'Status', 'Status',
'Security Groups', 'Security Groups',
'Device Owner', 'Device Owner',
'Tags',
) )
data = [] data = []
@ -625,6 +640,7 @@ class TestListPort(TestPort):
prt.status, prt.status,
utils.format_list(prt.security_group_ids), utils.format_list(prt.security_group_ids),
prt.device_owner, prt.device_owner,
utils.format_list(prt.tags),
)) ))
def setUp(self): def setUp(self):
@ -642,6 +658,8 @@ class TestListPort(TestPort):
self.network.find_router = mock.Mock(return_value=fake_router) self.network.find_router = mock.Mock(return_value=fake_router)
self.network.find_network = mock.Mock(return_value=fake_network) self.network.find_network = mock.Mock(return_value=fake_network)
self.app.client_manager.compute = mock.Mock() self.app.client_manager.compute = mock.Mock()
# TestUnsetTagMixin
self._tag_list_resource_mock = self.network.ports
def test_port_list_no_options(self): def test_port_list_no_options(self):
arglist = [] arglist = []
@ -902,9 +920,9 @@ class TestListPort(TestPort):
self.assertEqual(self.data, list(data)) self.assertEqual(self.data, list(data))
class TestSetPort(TestPort): class TestSetPort(TestPort, _test_tag.TestSetTagMixin):
_port = network_fakes.FakePort.create_one_port() _port = network_fakes.FakePort.create_one_port({'tags': ['green', 'red']})
def setUp(self): def setUp(self):
super(TestSetPort, self).setUp() super(TestSetPort, self).setUp()
@ -912,9 +930,14 @@ class TestSetPort(TestPort):
self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) self.network.find_subnet = mock.Mock(return_value=self.fake_subnet)
self.network.find_port = mock.Mock(return_value=self._port) self.network.find_port = mock.Mock(return_value=self._port)
self.network.update_port = mock.Mock(return_value=None) self.network.update_port = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = port.SetPort(self.app, self.namespace) self.cmd = port.SetPort(self.app, self.namespace)
# TestSetTagMixin
self._tag_resource_name = 'port'
self._tag_test_resource = self._port
self._tag_update_resource_mock = self.network.update_port
def test_set_port_defaults(self): def test_set_port_defaults(self):
arglist = [ arglist = [
@ -926,8 +949,8 @@ class TestSetPort(TestPort):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {} self.assertFalse(self.network.update_port.called)
self.network.update_port.assert_called_once_with(self._port, **attrs) self.assertFalse(self.network.set_tags.called)
self.assertIsNone(result) self.assertIsNone(result)
def test_set_port_fixed_ip(self): def test_set_port_fixed_ip(self):
@ -1412,6 +1435,7 @@ class TestShowPort(TestPort):
# The port to show. # The port to show.
_port = network_fakes.FakePort.create_one_port() _port = network_fakes.FakePort.create_one_port()
columns, data = TestPort._get_common_cols_data(_port)
def setUp(self): def setUp(self):
super(TestShowPort, self).setUp() super(TestShowPort, self).setUp()
@ -1442,12 +1466,11 @@ class TestShowPort(TestPort):
self.network.find_port.assert_called_once_with( self.network.find_port.assert_called_once_with(
self._port.name, ignore_missing=False) self._port.name, ignore_missing=False)
ref_columns, ref_data = self._get_common_cols_data(self._port) self.assertEqual(self.columns, columns)
self.assertEqual(ref_columns, columns) self.assertEqual(self.data, data)
self.assertEqual(ref_data, data)
class TestUnsetPort(TestPort): class TestUnsetPort(TestPort, _test_tag.TestUnsetTagMixin):
def setUp(self): def setUp(self):
super(TestUnsetPort, self).setUp() super(TestUnsetPort, self).setUp()
@ -1456,14 +1479,20 @@ class TestUnsetPort(TestPort):
'ip_address': '0.0.0.1'}, 'ip_address': '0.0.0.1'},
{'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152', {'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152',
'ip_address': '1.0.0.0'}], 'ip_address': '1.0.0.0'}],
'binding:profile': {'batman': 'Joker', 'Superman': 'LexLuthor'}}) 'binding:profile': {'batman': 'Joker', 'Superman': 'LexLuthor'},
'tags': ['green', 'red'], })
self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet(
{'id': '042eb10a-3a18-4658-ab-cf47c8d03152'}) {'id': '042eb10a-3a18-4658-ab-cf47c8d03152'})
self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) self.network.find_subnet = mock.Mock(return_value=self.fake_subnet)
self.network.find_port = mock.Mock(return_value=self._testport) self.network.find_port = mock.Mock(return_value=self._testport)
self.network.update_port = mock.Mock(return_value=None) self.network.update_port = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = port.UnsetPort(self.app, self.namespace) self.cmd = port.UnsetPort(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_resource_name = 'port'
self._tag_test_resource = self._testport
self._tag_update_resource_mock = self.network.update_port
def test_unset_port_parameters(self): def test_unset_port_parameters(self):
arglist = [ arglist = [

View File

@ -19,6 +19,7 @@ from osc_lib import utils as osc_utils
from openstackclient.network.v2 import router from openstackclient.network.v2 import router
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import _test_tag
from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit import utils as tests_utils
@ -111,7 +112,7 @@ class TestAddSubnetToRouter(TestRouter):
self.assertIsNone(result) self.assertIsNone(result)
class TestCreateRouter(TestRouter): class TestCreateRouter(TestRouter, _test_tag.TestCreateTagMixin):
# The new router created. # The new router created.
new_router = network_fakes.FakeRouter.create_one_router() new_router = network_fakes.FakeRouter.create_one_router()
@ -129,6 +130,7 @@ class TestCreateRouter(TestRouter):
'project_id', 'project_id',
'routes', 'routes',
'status', 'status',
'tags',
) )
data = ( data = (
router._format_admin_state(new_router.admin_state_up), router._format_admin_state(new_router.admin_state_up),
@ -143,22 +145,42 @@ class TestCreateRouter(TestRouter):
new_router.tenant_id, new_router.tenant_id,
router._format_routes(new_router.routes), router._format_routes(new_router.routes),
new_router.status, new_router.status,
osc_utils.format_list(new_router.tags),
) )
def setUp(self): def setUp(self):
super(TestCreateRouter, self).setUp() super(TestCreateRouter, self).setUp()
self.network.create_router = mock.Mock(return_value=self.new_router) self.network.create_router = mock.Mock(return_value=self.new_router)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = router.CreateRouter(self.app, self.namespace) self.cmd = router.CreateRouter(self.app, self.namespace)
# TestCreateTagMixin
self._tag_test_resource = self.new_router
self._tag_create_resource_mock = self.network.create_router
self._tag_create_required_arglist = [
self.new_router.name,
]
self._tag_create_required_verifylist = [
('name', self.new_router.name),
('enable', True),
('distributed', False),
('ha', False),
]
self._tag_create_required_attrs = {
'admin_state_up': True,
'name': self.new_router.name,
}
def test_create_no_options(self): def test_create_no_options(self):
arglist = [] arglist = []
verifylist = [] verifylist = []
self.assertRaises(tests_utils.ParserException, self.check_parser, self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist) self.cmd, arglist, verifylist)
self.assertFalse(self.network.set_tags.called)
def test_create_default_options(self): def test_create_default_options(self):
arglist = [ arglist = [
@ -178,6 +200,7 @@ class TestCreateRouter(TestRouter):
'admin_state_up': True, 'admin_state_up': True,
'name': self.new_router.name, 'name': self.new_router.name,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
@ -345,7 +368,7 @@ class TestDeleteRouter(TestRouter):
) )
class TestListRouter(TestRouter): class TestListRouter(TestRouter, _test_tag.TestListTagMixin):
# The routers going to be listed up. # The routers going to be listed up.
routers = network_fakes.FakeRouter.create_routers(count=3) routers = network_fakes.FakeRouter.create_routers(count=3)
@ -363,11 +386,13 @@ class TestListRouter(TestRouter):
columns_long = columns + ( columns_long = columns + (
'Routes', 'Routes',
'External gateway info', 'External gateway info',
'Availability zones' 'Availability zones',
'Tags',
) )
columns_long_no_az = columns + ( columns_long_no_az = columns + (
'Routes', 'Routes',
'External gateway info', 'External gateway info',
'Tags',
) )
data = [] data = []
@ -404,6 +429,7 @@ class TestListRouter(TestRouter):
router._format_routes(r.routes), router._format_routes(r.routes),
router._format_external_gateway_info(r.external_gateway_info), router._format_external_gateway_info(r.external_gateway_info),
osc_utils.format_list(r.availability_zones), osc_utils.format_list(r.availability_zones),
osc_utils.format_list(r.tags),
) )
) )
data_long_no_az = [] data_long_no_az = []
@ -413,6 +439,7 @@ class TestListRouter(TestRouter):
data[i] + ( data[i] + (
router._format_routes(r.routes), router._format_routes(r.routes),
router._format_external_gateway_info(r.external_gateway_info), router._format_external_gateway_info(r.external_gateway_info),
osc_utils.format_list(r.tags),
) )
) )
@ -432,6 +459,9 @@ class TestListRouter(TestRouter):
self.network.get_agent = mock.Mock(return_value=self._testagent) self.network.get_agent = mock.Mock(return_value=self._testagent)
self.network.get_router = mock.Mock(return_value=self.routers[0]) self.network.get_router = mock.Mock(return_value=self.routers[0])
# TestListTagMixin
self._tag_list_resource_mock = self.network.routers
def test_router_list_no_options(self): def test_router_list_no_options(self):
arglist = [] arglist = []
verifylist = [ verifylist = [
@ -684,26 +714,33 @@ class TestRemoveSubnetFromRouter(TestRouter):
self.assertIsNone(result) self.assertIsNone(result)
class TestSetRouter(TestRouter): class TestSetRouter(TestRouter, _test_tag.TestSetTagMixin):
# The router to set. # The router to set.
_default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'} _default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'}
_network = network_fakes.FakeNetwork.create_one_network() _network = network_fakes.FakeNetwork.create_one_network()
_subnet = network_fakes.FakeSubnet.create_one_subnet() _subnet = network_fakes.FakeSubnet.create_one_subnet()
_router = network_fakes.FakeRouter.create_one_router( _router = network_fakes.FakeRouter.create_one_router(
attrs={'routes': [_default_route]} attrs={'routes': [_default_route],
'tags': ['green', 'red']}
) )
def setUp(self): def setUp(self):
super(TestSetRouter, self).setUp() super(TestSetRouter, self).setUp()
self.network.router_add_gateway = mock.Mock() self.network.router_add_gateway = mock.Mock()
self.network.update_router = mock.Mock(return_value=None) self.network.update_router = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_router = mock.Mock(return_value=self._router) self.network.find_router = mock.Mock(return_value=self._router)
self.network.find_network = mock.Mock(return_value=self._network) self.network.find_network = mock.Mock(return_value=self._network)
self.network.find_subnet = mock.Mock(return_value=self._subnet) self.network.find_subnet = mock.Mock(return_value=self._subnet)
# Get the command object to test # Get the command object to test
self.cmd = router.SetRouter(self.app, self.namespace) self.cmd = router.SetRouter(self.app, self.namespace)
# TestSetTagMixin
self._tag_resource_name = 'router'
self._tag_test_resource = self._router
self._tag_update_resource_mock = self.network.update_router
def test_set_this(self): def test_set_this(self):
arglist = [ arglist = [
self._router.name, self._router.name,
@ -902,9 +939,8 @@ class TestSetRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {} self.assertFalse(self.network.update_router.called)
self.network.update_router.assert_called_once_with( self.assertFalse(self.network.set_tags.called)
self._router, **attrs)
self.assertIsNone(result) self.assertIsNone(result)
def test_wrong_gateway_params(self): def test_wrong_gateway_params(self):
@ -1030,6 +1066,7 @@ class TestShowRouter(TestRouter):
'project_id', 'project_id',
'routes', 'routes',
'status', 'status',
'tags',
) )
data = ( data = (
router._format_admin_state(_router.admin_state_up), router._format_admin_state(_router.admin_state_up),
@ -1044,6 +1081,7 @@ class TestShowRouter(TestRouter):
_router.tenant_id, _router.tenant_id,
router._format_routes(_router.routes), router._format_routes(_router.routes),
_router.status, _router.status,
osc_utils.format_list(_router.tags),
) )
def setUp(self): def setUp(self):
@ -1086,12 +1124,18 @@ class TestUnsetRouter(TestRouter):
{'routes': [{"destination": "192.168.101.1/24", {'routes': [{"destination": "192.168.101.1/24",
"nexthop": "172.24.4.3"}, "nexthop": "172.24.4.3"},
{"destination": "192.168.101.2/24", {"destination": "192.168.101.2/24",
"nexthop": "172.24.4.3"}], }) "nexthop": "172.24.4.3"}],
'tags': ['green', 'red'], })
self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet()
self.network.find_router = mock.Mock(return_value=self._testrouter) self.network.find_router = mock.Mock(return_value=self._testrouter)
self.network.update_router = mock.Mock(return_value=None) self.network.update_router = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = router.UnsetRouter(self.app, self.namespace) self.cmd = router.UnsetRouter(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_resource_name = 'router'
self._tag_test_resource = self._testrouter
self._tag_update_resource_mock = self.network.update_router
def test_unset_router_params(self): def test_unset_router_params(self):
arglist = [ arglist = [

View File

@ -19,6 +19,7 @@ from osc_lib import utils
from openstackclient.network.v2 import subnet as subnet_v2 from openstackclient.network.v2 import subnet as subnet_v2
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import _test_tag
from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit import utils as tests_utils
@ -36,7 +37,7 @@ class TestSubnet(network_fakes.TestNetworkV2):
self.domains_mock = self.app.client_manager.identity.domains self.domains_mock = self.app.client_manager.identity.domains
class TestCreateSubnet(TestSubnet): class TestCreateSubnet(TestSubnet, _test_tag.TestCreateTagMixin):
project = identity_fakes_v3.FakeProject.create_one_project() project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain() domain = identity_fakes_v3.FakeDomain.create_one_domain()
@ -125,6 +126,7 @@ class TestCreateSubnet(TestSubnet):
'segment_id', 'segment_id',
'service_types', 'service_types',
'subnetpool_id', 'subnetpool_id',
'tags',
) )
data = ( data = (
@ -145,6 +147,7 @@ class TestCreateSubnet(TestSubnet):
_subnet.segment_id, _subnet.segment_id,
utils.format_list(_subnet.service_types), utils.format_list(_subnet.service_types),
_subnet.subnetpool_id, _subnet.subnetpool_id,
utils.format_list(_subnet.tags),
) )
data_subnet_pool = ( data_subnet_pool = (
@ -165,6 +168,7 @@ class TestCreateSubnet(TestSubnet):
_subnet_from_pool.segment_id, _subnet_from_pool.segment_id,
utils.format_list(_subnet_from_pool.service_types), utils.format_list(_subnet_from_pool.service_types),
_subnet_from_pool.subnetpool_id, _subnet_from_pool.subnetpool_id,
utils.format_list(_subnet.tags),
) )
data_ipv6 = ( data_ipv6 = (
@ -185,6 +189,7 @@ class TestCreateSubnet(TestSubnet):
_subnet_ipv6.segment_id, _subnet_ipv6.segment_id,
utils.format_list(_subnet_ipv6.service_types), utils.format_list(_subnet_ipv6.service_types),
_subnet_ipv6.subnetpool_id, _subnet_ipv6.subnetpool_id,
utils.format_list(_subnet.tags),
) )
def setUp(self): def setUp(self):
@ -197,6 +202,8 @@ class TestCreateSubnet(TestSubnet):
self.domains_mock.get.return_value = self.domain self.domains_mock.get.return_value = self.domain
# Mock SDK calls for all tests. # Mock SDK calls for all tests.
self.network.create_subnet = mock.Mock(return_value=self._subnet)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_network = mock.Mock(return_value=self._network) self.network.find_network = mock.Mock(return_value=self._network)
self.network.find_segment = mock.Mock( self.network.find_segment = mock.Mock(
return_value=self._network_segment return_value=self._network_segment
@ -205,6 +212,28 @@ class TestCreateSubnet(TestSubnet):
return_value=self._subnet_pool return_value=self._subnet_pool
) )
# TestUnsetTagMixin
self._tag_test_resource = self._subnet
self._tag_create_resource_mock = self.network.create_subnet
self._tag_create_required_arglist = [
"--subnet-range", self._subnet.cidr,
"--network", self._subnet.network_id,
self._subnet.name,
]
self._tag_create_required_verifylist = [
('name', self._subnet.name),
('subnet_range', self._subnet.cidr),
('network', self._subnet.network_id),
('ip_version', self._subnet.ip_version),
('gateway', 'auto'),
]
self._tag_create_required_attrs = {
'cidr': self._subnet.cidr,
'ip_version': self._subnet.ip_version,
'name': self._subnet.name,
'network_id': self._subnet.network_id,
}
def test_create_no_options(self): def test_create_no_options(self):
arglist = [] arglist = []
verifylist = [] verifylist = []
@ -213,10 +242,11 @@ class TestCreateSubnet(TestSubnet):
# throw a "ParserExecption" # throw a "ParserExecption"
self.assertRaises(tests_utils.ParserException, self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, arglist, verifylist) self.check_parser, self.cmd, arglist, verifylist)
self.assertFalse(self.network.create_subnet.called)
self.assertFalse(self.network.set_tags.called)
def test_create_default_options(self): def test_create_default_options(self):
# Mock SDK calls for this test. # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet)
self._network.id = self._subnet.network_id self._network.id = self._subnet.network_id
arglist = [ arglist = [
@ -230,7 +260,6 @@ class TestCreateSubnet(TestSubnet):
('network', self._subnet.network_id), ('network', self._subnet.network_id),
('ip_version', self._subnet.ip_version), ('ip_version', self._subnet.ip_version),
('gateway', 'auto'), ('gateway', 'auto'),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -242,13 +271,14 @@ class TestCreateSubnet(TestSubnet):
'name': self._subnet.name, 'name': self._subnet.name,
'network_id': self._subnet.network_id, 'network_id': self._subnet.network_id,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
def test_create_from_subnet_pool_options(self): def test_create_from_subnet_pool_options(self):
# Mock SDK calls for this test. # Mock SDK calls for this test.
self.network.create_subnet = \ self.network.create_subnet.return_value = self._subnet_from_pool
mock.Mock(return_value=self._subnet_from_pool) self.network.set_tags = mock.Mock(return_value=None)
self._network.id = self._subnet_from_pool.network_id self._network.id = self._subnet_from_pool.network_id
arglist = [ arglist = [
@ -309,7 +339,7 @@ class TestCreateSubnet(TestSubnet):
def test_create_options_subnet_range_ipv6(self): def test_create_options_subnet_range_ipv6(self):
# Mock SDK calls for this test. # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6) self.network.create_subnet.return_value = self._subnet_ipv6
self._network.id = self._subnet_ipv6.network_id self._network.id = self._subnet_ipv6.network_id
arglist = [ arglist = [
@ -376,12 +406,12 @@ class TestCreateSubnet(TestSubnet):
'allocation_pools': self._subnet_ipv6.allocation_pools, 'allocation_pools': self._subnet_ipv6.allocation_pools,
'service_types': self._subnet_ipv6.service_types, 'service_types': self._subnet_ipv6.service_types,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data_ipv6, data) self.assertEqual(self.data_ipv6, data)
def test_create_with_network_segment(self): def test_create_with_network_segment(self):
# Mock SDK calls for this test. # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet)
self._network.id = self._subnet.network_id self._network.id = self._subnet.network_id
arglist = [ arglist = [
@ -410,12 +440,12 @@ class TestCreateSubnet(TestSubnet):
'network_id': self._subnet.network_id, 'network_id': self._subnet.network_id,
'segment_id': self._network_segment.id, 'segment_id': self._network_segment.id,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
def test_create_with_description(self): def test_create_with_description(self):
# Mock SDK calls for this test. # Mock SDK calls for this test.
self.network.create_subnet = mock.Mock(return_value=self._subnet)
self._network.id = self._subnet.network_id self._network.id = self._subnet.network_id
arglist = [ arglist = [
@ -444,6 +474,7 @@ class TestCreateSubnet(TestSubnet):
'network_id': self._subnet.network_id, 'network_id': self._subnet.network_id,
'description': self._subnet.description, 'description': self._subnet.description,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
@ -527,7 +558,7 @@ class TestDeleteSubnet(TestSubnet):
) )
class TestListSubnet(TestSubnet): class TestListSubnet(TestSubnet, _test_tag.TestListTagMixin):
# The subnets going to be listed up. # The subnets going to be listed up.
_subnet = network_fakes.FakeSubnet.create_subnets(count=3) _subnet = network_fakes.FakeSubnet.create_subnets(count=3)
@ -546,6 +577,7 @@ class TestListSubnet(TestSubnet):
'IP Version', 'IP Version',
'Gateway', 'Gateway',
'Service Types', 'Service Types',
'Tags',
) )
data = [] data = []
@ -572,6 +604,7 @@ class TestListSubnet(TestSubnet):
subnet.ip_version, subnet.ip_version,
subnet.gateway_ip, subnet.gateway_ip,
utils.format_list(subnet.service_types), utils.format_list(subnet.service_types),
utils.format_list(subnet.tags),
)) ))
def setUp(self): def setUp(self):
@ -582,6 +615,9 @@ class TestListSubnet(TestSubnet):
self.network.subnets = mock.Mock(return_value=self._subnet) self.network.subnets = mock.Mock(return_value=self._subnet)
# TestUnsetTagMixin
self._tag_list_resource_mock = self.network.subnets
def test_subnet_list_no_options(self): def test_subnet_list_no_options(self):
arglist = [] arglist = []
verifylist = [ verifylist = [
@ -802,15 +838,21 @@ class TestListSubnet(TestSubnet):
self.assertEqual(self.data, list(data)) self.assertEqual(self.data, list(data))
class TestSetSubnet(TestSubnet): class TestSetSubnet(TestSubnet, _test_tag.TestSetTagMixin):
_subnet = network_fakes.FakeSubnet.create_one_subnet() _subnet = network_fakes.FakeSubnet.create_one_subnet(
{'tags': ['green', 'red']})
def setUp(self): def setUp(self):
super(TestSetSubnet, self).setUp() super(TestSetSubnet, self).setUp()
self.network.update_subnet = mock.Mock(return_value=None) self.network.update_subnet = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_subnet = mock.Mock(return_value=self._subnet) self.network.find_subnet = mock.Mock(return_value=self._subnet)
self.cmd = subnet_v2.SetSubnet(self.app, self.namespace) self.cmd = subnet_v2.SetSubnet(self.app, self.namespace)
# TestSetTagMixin
self._tag_resource_name = 'subnet'
self._tag_test_resource = self._subnet
self._tag_update_resource_mock = self.network.update_subnet
def test_set_this(self): def test_set_this(self):
arglist = [ arglist = [
@ -867,8 +909,8 @@ class TestSetSubnet(TestSubnet):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {} self.assertFalse(self.network.update_subnet.called)
self.network.update_subnet.assert_called_with(self._subnet, **attrs) self.assertFalse(self.network.set_tags.called)
self.assertIsNone(result) self.assertIsNone(result)
def test_append_options(self): def test_append_options(self):
@ -982,6 +1024,7 @@ class TestShowSubnet(TestSubnet):
'segment_id', 'segment_id',
'service_types', 'service_types',
'subnetpool_id', 'subnetpool_id',
'tags',
) )
data = ( data = (
@ -1002,6 +1045,7 @@ class TestShowSubnet(TestSubnet):
_subnet.segment_id, _subnet.segment_id,
utils.format_list(_subnet.service_types), utils.format_list(_subnet.service_types),
_subnet.subnetpool_id, _subnet.subnetpool_id,
utils.format_list(_subnet.tags),
) )
def setUp(self): def setUp(self):
@ -1039,7 +1083,7 @@ class TestShowSubnet(TestSubnet):
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
class TestUnsetSubnet(TestSubnet): class TestUnsetSubnet(TestSubnet, _test_tag.TestUnsetTagMixin):
def setUp(self): def setUp(self):
super(TestUnsetSubnet, self).setUp() super(TestUnsetSubnet, self).setUp()
@ -1055,11 +1099,17 @@ class TestUnsetSubnet(TestSubnet):
{'start': '8.8.8.160', {'start': '8.8.8.160',
'end': '8.8.8.170'}], 'end': '8.8.8.170'}],
'service_types': ['network:router_gateway', 'service_types': ['network:router_gateway',
'network:floatingip_agent_gateway'], }) 'network:floatingip_agent_gateway'],
'tags': ['green', 'red'], })
self.network.find_subnet = mock.Mock(return_value=self._testsubnet) self.network.find_subnet = mock.Mock(return_value=self._testsubnet)
self.network.update_subnet = mock.Mock(return_value=None) self.network.update_subnet = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = subnet_v2.UnsetSubnet(self.app, self.namespace) self.cmd = subnet_v2.UnsetSubnet(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_resource_name = 'subnet'
self._tag_test_resource = self._testsubnet
self._tag_update_resource_mock = self.network.update_subnet
def test_unset_subnet_params(self): def test_unset_subnet_params(self):
arglist = [ arglist = [

View File

@ -20,6 +20,7 @@ from osc_lib import utils
from openstackclient.network.v2 import subnet_pool from openstackclient.network.v2 import subnet_pool
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import _test_tag
from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit import utils as tests_utils
@ -37,7 +38,7 @@ class TestSubnetPool(network_fakes.TestNetworkV2):
self.domains_mock = self.app.client_manager.identity.domains self.domains_mock = self.app.client_manager.identity.domains
class TestCreateSubnetPool(TestSubnetPool): class TestCreateSubnetPool(TestSubnetPool, _test_tag.TestCreateTagMixin):
project = identity_fakes_v3.FakeProject.create_one_project() project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain() domain = identity_fakes_v3.FakeDomain.create_one_domain()
@ -60,6 +61,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'prefixes', 'prefixes',
'project_id', 'project_id',
'shared', 'shared',
'tags',
) )
data = ( data = (
_subnet_pool.address_scope_id, _subnet_pool.address_scope_id,
@ -75,6 +77,7 @@ class TestCreateSubnetPool(TestSubnetPool):
utils.format_list(_subnet_pool.prefixes), utils.format_list(_subnet_pool.prefixes),
_subnet_pool.project_id, _subnet_pool.project_id,
_subnet_pool.shared, _subnet_pool.shared,
utils.format_list(_subnet_pool.tags),
) )
def setUp(self): def setUp(self):
@ -82,6 +85,7 @@ class TestCreateSubnetPool(TestSubnetPool):
self.network.create_subnet_pool = mock.Mock( self.network.create_subnet_pool = mock.Mock(
return_value=self._subnet_pool) return_value=self._subnet_pool)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace) self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace)
@ -92,12 +96,29 @@ class TestCreateSubnetPool(TestSubnetPool):
self.projects_mock.get.return_value = self.project self.projects_mock.get.return_value = self.project
self.domains_mock.get.return_value = self.domain self.domains_mock.get.return_value = self.domain
# TestUnsetTagMixin
self._tag_test_resource = self._subnet_pool
self._tag_create_resource_mock = self.network.create_subnet_pool
self._tag_create_required_arglist = [
'--pool-prefix', '10.0.10.0/24',
self._subnet_pool.name,
]
self._tag_create_required_verifylist = [
('prefixes', ['10.0.10.0/24']),
('name', self._subnet_pool.name),
]
self._tag_create_required_attrs = {
'prefixes': ['10.0.10.0/24'],
'name': self._subnet_pool.name,
}
def test_create_no_options(self): def test_create_no_options(self):
arglist = [] arglist = []
verifylist = [] verifylist = []
self.assertRaises(tests_utils.ParserException, self.check_parser, self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist) self.cmd, arglist, verifylist)
self.assertFalse(self.network.set_tags.called)
def test_create_no_pool_prefix(self): def test_create_no_pool_prefix(self):
"""Make sure --pool-prefix is a required argument""" """Make sure --pool-prefix is a required argument"""
@ -127,6 +148,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'prefixes': ['10.0.10.0/24'], 'prefixes': ['10.0.10.0/24'],
'name': self._subnet_pool.name, 'name': self._subnet_pool.name,
}) })
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
@ -374,7 +396,7 @@ class TestDeleteSubnetPool(TestSubnetPool):
) )
class TestListSubnetPool(TestSubnetPool): class TestListSubnetPool(TestSubnetPool, _test_tag.TestListTagMixin):
# The subnet pools going to be listed up. # The subnet pools going to be listed up.
_subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=3) _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=3)
@ -388,6 +410,7 @@ class TestListSubnetPool(TestSubnetPool):
'Address Scope', 'Address Scope',
'Default Subnet Pool', 'Default Subnet Pool',
'Shared', 'Shared',
'Tags',
) )
data = [] data = []
@ -408,6 +431,7 @@ class TestListSubnetPool(TestSubnetPool):
pool.address_scope_id, pool.address_scope_id,
pool.is_default, pool.is_default,
pool.shared, pool.shared,
utils.format_list(pool.tags),
)) ))
def setUp(self): def setUp(self):
@ -418,6 +442,9 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools = mock.Mock(return_value=self._subnet_pools) self.network.subnet_pools = mock.Mock(return_value=self._subnet_pools)
# TestUnsetTagMixin
self._tag_list_resource_mock = self.network.subnet_pools
def test_subnet_pool_list_no_option(self): def test_subnet_pool_list_no_option(self):
arglist = [] arglist = []
verifylist = [ verifylist = [
@ -585,11 +612,12 @@ class TestListSubnetPool(TestSubnetPool):
self.assertEqual(self.data, list(data)) self.assertEqual(self.data, list(data))
class TestSetSubnetPool(TestSubnetPool): class TestSetSubnetPool(TestSubnetPool, _test_tag.TestSetTagMixin):
# The subnet_pool to set. # The subnet_pool to set.
_subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool( _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool(
{'default_quota': 10}, {'default_quota': 10,
'tags': ['green', 'red']}
) )
_address_scope = network_fakes.FakeAddressScope.create_one_address_scope() _address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
@ -598,6 +626,7 @@ class TestSetSubnetPool(TestSubnetPool):
super(TestSetSubnetPool, self).setUp() super(TestSetSubnetPool, self).setUp()
self.network.update_subnet_pool = mock.Mock(return_value=None) self.network.update_subnet_pool = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
self.network.find_subnet_pool = mock.Mock( self.network.find_subnet_pool = mock.Mock(
return_value=self._subnet_pool) return_value=self._subnet_pool)
@ -608,6 +637,11 @@ class TestSetSubnetPool(TestSubnetPool):
# Get the command object to test # Get the command object to test
self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace) self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_resource_name = 'subnet_pool'
self._tag_test_resource = self._subnet_pool
self._tag_update_resource_mock = self.network.update_subnet_pool
def test_set_this(self): def test_set_this(self):
arglist = [ arglist = [
'--name', 'noob', '--name', 'noob',
@ -667,9 +701,8 @@ class TestSetSubnetPool(TestSubnetPool):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {} self.assertFalse(self.network.update_subnet_pool.called)
self.network.update_subnet_pool.assert_called_once_with( self.assertFalse(self.network.set_tags.called)
self._subnet_pool, **attrs)
self.assertIsNone(result) self.assertIsNone(result)
def test_set_len_negative(self): def test_set_len_negative(self):
@ -854,6 +887,7 @@ class TestShowSubnetPool(TestSubnetPool):
'prefixes', 'prefixes',
'project_id', 'project_id',
'shared', 'shared',
'tags',
) )
data = ( data = (
@ -870,6 +904,7 @@ class TestShowSubnetPool(TestSubnetPool):
utils.format_list(_subnet_pool.prefixes), utils.format_list(_subnet_pool.prefixes),
_subnet_pool.tenant_id, _subnet_pool.tenant_id,
_subnet_pool.shared, _subnet_pool.shared,
utils.format_list(_subnet_pool.tags),
) )
def setUp(self): def setUp(self):
@ -908,26 +943,36 @@ class TestShowSubnetPool(TestSubnetPool):
self.assertEqual(self.data, data) self.assertEqual(self.data, data)
class TestUnsetSubnetPool(TestSubnetPool): class TestUnsetSubnetPool(TestSubnetPool, _test_tag.TestUnsetTagMixin):
def setUp(self): def setUp(self):
super(TestUnsetSubnetPool, self).setUp() super(TestUnsetSubnetPool, self).setUp()
self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool(
{'prefixes': ['10.0.10.0/24', '10.1.10.0/24', {'prefixes': ['10.0.10.0/24', '10.1.10.0/24',
'10.2.10.0/24'], }) '10.2.10.0/24'],
'tags': ['green', 'red']})
self.network.find_subnet_pool = mock.Mock( self.network.find_subnet_pool = mock.Mock(
return_value=self._subnetpool) return_value=self._subnetpool)
self.network.update_subnet_pool = mock.Mock(return_value=None) self.network.update_subnet_pool = mock.Mock(return_value=None)
self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test # Get the command object to test
self.cmd = subnet_pool.UnsetSubnetPool(self.app, self.namespace) self.cmd = subnet_pool.UnsetSubnetPool(self.app, self.namespace)
# TestUnsetTagMixin
self._tag_resource_name = 'subnet_pool'
self._tag_test_resource = self._subnetpool
self._tag_update_resource_mock = self.network.update_subnet_pool
def test_unset_subnet_pool(self): def test_unset_subnet_pool(self):
arglist = [ arglist = [
'--pool-prefix', '10.0.10.0/24', '--pool-prefix', '10.0.10.0/24',
'--pool-prefix', '10.1.10.0/24', '--pool-prefix', '10.1.10.0/24',
self._subnetpool.name, self._subnetpool.name,
] ]
verifylist = [('prefixes', ['10.0.10.0/24', '10.1.10.0/24'])] verifylist = [
('prefixes', ['10.0.10.0/24', '10.1.10.0/24']),
('subnet_pool', self._subnetpool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
attrs = {'prefixes': ['10.2.10.0/24']} attrs = {'prefixes': ['10.2.10.0/24']}
@ -940,7 +985,10 @@ class TestUnsetSubnetPool(TestSubnetPool):
'--pool-prefix', '10.100.1.1/25', '--pool-prefix', '10.100.1.1/25',
self._subnetpool.name, self._subnetpool.name,
] ]
verifylist = [('prefixes', ['10.100.1.1/25'])] verifylist = [
('prefixes', ['10.100.1.1/25']),
('subnet_pool', self._subnetpool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exceptions.CommandError, self.assertRaises(exceptions.CommandError,
self.cmd.take_action, self.cmd.take_action,

View File

@ -25,6 +25,12 @@ class ParserException(Exception):
pass pass
class CompareBySet(list):
"""Class to compare value using set."""
def __eq__(self, other):
return set(self) == set(other)
class TestCase(testtools.TestCase): class TestCase(testtools.TestCase):
def setUp(self): def setUp(self):

View File

@ -0,0 +1,13 @@
---
features:
- |
Added support for ``tags`` to the following resources:
``network``, ``subnet``, ``port``, ``router`` and ``subnet pool``.
[Blueprint :oscbp:`neutron-client-tag`]
- Add ``--tag`` and ``--no-tag`` options to corresponding "create" commands.
- Add ``--tag`` and ``--no-tag`` options to corresponding "set" commands.
- Add ``--tag`` and ``--all-tag`` options to corresponding "unset" commands.
(``network unset`` command is introduced to support the tag operation)
- Add ``--tags``, ``--any-tags``, ``--not-tags`` and ``--not-any-tags``
options to corresponding "list" commands.

View File

@ -395,6 +395,7 @@ openstack.network.v2 =
network_list = openstackclient.network.v2.network:ListNetwork network_list = openstackclient.network.v2.network:ListNetwork
network_set = openstackclient.network.v2.network:SetNetwork network_set = openstackclient.network.v2.network:SetNetwork
network_show = openstackclient.network.v2.network:ShowNetwork network_show = openstackclient.network.v2.network:ShowNetwork
network_unset = openstackclient.network.v2.network:UnsetNetwork
network_meter_create = openstackclient.network.v2.network_meter:CreateMeter network_meter_create = openstackclient.network.v2.network_meter:CreateMeter
network_meter_delete = openstackclient.network.v2.network_meter:DeleteMeter network_meter_delete = openstackclient.network.v2.network_meter:DeleteMeter