Browse Source

Add tag support for Octavia resources

Add tag support for load balancers, listeners, pools, health monitors,
members, l7policies and l7rules.
Tags can be set when creating a resource, and can be modified using
set/unset CLI calls.
Filtering by tags is also supported when listing resources.

This commit also fixes some errors when using set/unset l7rule CLI with
the --wait flag (l7rule_id and l7policy_id parameters were swapped in
partial function calls).

Story: 2008347
Task: 41249

Change-Id: I1d5119cb5ce9da0fd6c7a39aeb371a507607b9d7
changes/68/762668/1
Gregory Thiemonge 7 months ago
parent
commit
9c4b85a523
18 changed files with 1513 additions and 61 deletions
  1. +13
    -8
      octaviaclient/osc/v2/constants.py
  2. +24
    -3
      octaviaclient/osc/v2/health_monitor.py
  3. +24
    -3
      octaviaclient/osc/v2/l7policy.py
  4. +39
    -16
      octaviaclient/osc/v2/l7rule.py
  5. +25
    -3
      octaviaclient/osc/v2/listener.py
  6. +24
    -3
      octaviaclient/osc/v2/load_balancer.py
  7. +35
    -13
      octaviaclient/osc/v2/member.py
  8. +24
    -3
      octaviaclient/osc/v2/pool.py
  9. +44
    -3
      octaviaclient/osc/v2/utils.py
  10. +5
    -2
      octaviaclient/tests/unit/osc/v2/constants.py
  11. +157
    -0
      octaviaclient/tests/unit/osc/v2/test_health_monitor.py
  12. +168
    -0
      octaviaclient/tests/unit/osc/v2/test_l7policy.py
  13. +227
    -3
      octaviaclient/tests/unit/osc/v2/test_l7rule.py
  14. +153
    -0
      octaviaclient/tests/unit/osc/v2/test_listener.py
  15. +190
    -0
      octaviaclient/tests/unit/osc/v2/test_load_balancer.py
  16. +196
    -1
      octaviaclient/tests/unit/osc/v2/test_member.py
  17. +153
    -0
      octaviaclient/tests/unit/osc/v2/test_pool.py
  18. +12
    -0
      releasenotes/notes/add-tag-support-01087c4b3c4360dc.yaml

+ 13
- 8
octaviaclient/osc/v2/constants.py View File

@ -32,6 +32,7 @@ LOAD_BALANCER_ROWS = (
'vip_port_id',
'vip_qos_policy_id',
'vip_subnet_id',
'tags',
)
LOAD_BALANCER_COLUMNS = (
@ -79,7 +80,8 @@ LISTENER_ROWS = (
'allowed_cidrs',
'tls_ciphers',
'tls_versions',
'alpn_protocols')
'alpn_protocols',
'tags')
LISTENER_COLUMNS = (
'id',
@ -112,7 +114,8 @@ POOL_ROWS = (
'crl_container_ref',
'tls_enabled',
'tls_ciphers',
'tls_versions')
'tls_versions',
'tags')
POOL_COLUMNS = (
'id',
@ -138,8 +141,8 @@ MEMBER_ROWS = (
'weight',
'monitor_port',
'monitor_address',
'backup'
)
'backup',
'tags')
MEMBER_COLUMNS = (
'id',
@ -168,7 +171,8 @@ L7POLICY_ROWS = (
'id',
'operating_status',
'name',
'redirect_http_code')
'redirect_http_code',
'tags')
L7POLICY_COLUMNS = (
'id',
@ -191,7 +195,8 @@ L7RULE_ROWS = (
'project_id',
'type',
'id',
'operating_status')
'operating_status',
'tags')
L7RULE_COLUMNS = (
'id',
@ -223,8 +228,8 @@ MONITOR_ROWS = (
'id',
'operating_status',
'http_version',
'domain_name'
)
'domain_name',
'tags')
MONITOR_COLUMNS = (
'id',


+ 24
- 3
octaviaclient/osc/v2/health_monitor.py View File

@ -21,6 +21,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -140,6 +141,9 @@ class CreateHealthMonitor(command.ShowOne):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_create(
parser, 'health monitor')
return parser
def take_action(self, parsed_args):
@ -164,7 +168,8 @@ class CreateHealthMonitor(command.ShowOne):
data['healthmonitor']['id']))
}
formatters = {'pools': v2_utils.format_list}
formatters = {'pools': v2_utils.format_list,
'tags': v2_utils.format_list_flat}
return (rows,
(utils.get_dict_properties(data['healthmonitor'],
@ -214,6 +219,8 @@ class ListHealthMonitor(lister.Lister):
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
_tag.add_tag_filtering_option_to_parser(parser, 'health monitor')
return parser
def take_action(self, parsed_args):
@ -262,7 +269,8 @@ class ShowHealthMonitor(command.ShowOne):
data = self.app.client_manager.load_balancer.health_monitor_show(
health_monitor_id=health_monitor_id,
)
formatters = {'pools': v2_utils.format_list}
formatters = {'pools': v2_utils.format_list,
'tags': v2_utils.format_list_flat}
return (rows,
(utils.get_dict_properties(data, rows, formatters=formatters)))
@ -364,6 +372,8 @@ class SetHealthMonitor(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_set(parser, 'health monitor')
return parser
def take_action(self, parsed_args):
@ -372,6 +382,10 @@ class SetHealthMonitor(command.Command):
hm_id = attrs.pop('health_monitor_id')
v2_utils.set_tags_for_set(
self.app.client_manager.load_balancer.health_monitor_show,
hm_id, attrs, clear_tags=parsed_args.no_tag)
body = {'healthmonitor': attrs}
self.app.client_manager.load_balancer.health_monitor_set(
@ -437,17 +451,24 @@ class UnsetHealthMonitor(command.Command):
action='store_true',
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'health monitor')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
hm_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.health_monitor_list,
'healthmonitors', parsed_args.health_monitor)
v2_utils.set_tags_for_unset(
self.app.client_manager.load_balancer.health_monitor_show,
hm_id, unset_args, clear_tags=parsed_args.all_tag)
body = {'healthmonitor': unset_args}
self.app.client_manager.load_balancer.health_monitor_set(


+ 24
- 3
octaviaclient/osc/v2/l7policy.py View File

@ -20,6 +20,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -112,6 +113,9 @@ class CreateL7Policy(command.ShowOne):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_create(
parser, 'l7policy')
return parser
def take_action(self, parsed_args):
@ -138,7 +142,8 @@ class CreateL7Policy(command.ShowOne):
data['l7policy']['id']))
}
formatters = {'rules': v2_utils.format_list}
formatters = {'rules': v2_utils.format_list,
'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data['l7policy'], rows, formatters=formatters)))
@ -192,6 +197,8 @@ class ListL7Policy(lister.Lister):
"(name or ID)."
)
_tag.add_tag_filtering_option_to_parser(parser, 'l7policy')
return parser
def take_action(self, parsed_args):
@ -241,7 +248,8 @@ class ShowL7Policy(command.ShowOne):
data = self.app.client_manager.load_balancer.l7policy_show(
l7policy_id=l7policy_id,
)
formatters = {'rules': v2_utils.format_list}
formatters = {'rules': v2_utils.format_list,
'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data, rows, formatters=formatters)))
@ -325,6 +333,8 @@ class SetL7Policy(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_set(parser, 'l7policy')
return parser
def take_action(self, parsed_args):
@ -334,6 +344,10 @@ class SetL7Policy(command.Command):
validate.check_l7policy_attrs(attrs)
l7policy_id = attrs.pop('l7policy_id')
v2_utils.set_tags_for_set(
self.app.client_manager.load_balancer.l7policy_show,
l7policy_id, attrs, clear_tags=parsed_args.no_tag)
body = {'l7policy': attrs}
self.app.client_manager.load_balancer.l7policy_set(
@ -378,17 +392,24 @@ class UnsetL7Policy(command.Command):
action='store_true',
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'l7policy')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
policy_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.l7policy_list,
'l7policies', parsed_args.l7policy)
v2_utils.set_tags_for_unset(
self.app.client_manager.load_balancer.l7policy_show,
policy_id, unset_args, clear_tags=parsed_args.all_tag)
body = {'l7policy': unset_args}
self.app.client_manager.load_balancer.l7policy_set(


+ 39
- 16
octaviaclient/osc/v2/l7rule.py View File

@ -22,6 +22,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -95,6 +96,9 @@ class CreateL7Rule(command.ShowOne):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_create(
parser, 'l7rule')
return parser
def take_action(self, parsed_args):
@ -126,8 +130,10 @@ class CreateL7Rule(command.ShowOne):
l7policy_id, data['rule']['id']))
}
formatters = {'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data['rule'], rows, formatters={})))
data['rule'], rows, formatters=formatters)))
class DeleteL7Rule(command.Command):
@ -165,11 +171,11 @@ class DeleteL7Rule(command.Command):
if parsed_args.wait:
l7rule_show = functools.partial(
self.app.client_manager.load_balancer.l7rule_show,
attrs['l7policy_id']
attrs['l7rule_id']
)
v2_utils.wait_for_delete(
status_f=l7rule_show,
res_id=attrs['l7rule_id']
res_id=attrs['l7policy_id']
)
@ -185,6 +191,8 @@ class ListL7Rule(lister.Lister):
help='l7policy to list rules for (name or ID).'
)
_tag.add_tag_filtering_option_to_parser(parser, 'l7rule')
return parser
def take_action(self, parsed_args):
@ -192,7 +200,7 @@ class ListL7Rule(lister.Lister):
attrs = v2_utils.get_l7rule_attrs(self.app.client_manager, parsed_args)
data = self.app.client_manager.load_balancer.l7rule_list(
l7policy_id=attrs['l7policy_id']
**attrs
)
return (columns,
@ -237,9 +245,10 @@ class ShowL7Rule(command.ShowOne):
l7rule_id=attrs['l7rule_id'],
l7policy_id=attrs['l7policy_id']
)
formatters = {'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data, rows, formatters={})))
data, rows, formatters=formatters)))
class SetL7Rule(command.Command):
@ -307,6 +316,8 @@ class SetL7Rule(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_set(parser, 'l7rule')
return parser
def take_action(self, parsed_args):
@ -316,6 +327,15 @@ class SetL7Rule(command.Command):
l7policy_id = attrs.pop('l7policy_id')
l7rule_id = attrs.pop('l7rule_id')
# l7rule_id is the first argument in l7rule_show
l7rule_show = functools.partial(
self.app.client_manager.load_balancer.l7rule_show,
l7rule_id
)
v2_utils.set_tags_for_set(
l7rule_show, l7policy_id, attrs, clear_tags=parsed_args.no_tag)
body = {'rule': attrs}
self.app.client_manager.load_balancer.l7rule_set(
@ -325,13 +345,9 @@ class SetL7Rule(command.Command):
)
if parsed_args.wait:
l7rule_show = functools.partial(
self.app.client_manager.load_balancer.l7rule_show,
l7policy_id
)
v2_utils.wait_for_active(
status_f=l7rule_show,
res_id=l7rule_id
res_id=l7policy_id
)
@ -366,28 +382,35 @@ class UnsetL7Rule(command.Command):
action='store_true',
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'l7rule')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
policy_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.l7policy_list,
'l7policies', parsed_args.l7policy)
l7rule_show = functools.partial(
self.app.client_manager.load_balancer.l7rule_show,
parsed_args.l7rule_id
)
v2_utils.set_tags_for_unset(
l7rule_show, policy_id, unset_args,
clear_tags=parsed_args.all_tag)
body = {'rule': unset_args}
self.app.client_manager.load_balancer.l7rule_set(
l7policy_id=policy_id, l7rule_id=parsed_args.l7rule_id, json=body)
if parsed_args.wait:
l7rule_show = functools.partial(
self.app.client_manager.load_balancer.l7rule_show,
policy_id
)
v2_utils.wait_for_active(
status_f=l7rule_show,
res_id=parsed_args.l7rule_id
res_id=policy_id,
)

+ 25
- 3
octaviaclient/osc/v2/listener.py View File

@ -19,6 +19,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -200,6 +201,9 @@ class CreateListener(command.ShowOne):
"by the listener (can be set multiple times)."
)
_tag.add_tag_option_to_parser_for_create(
parser, 'listener')
return parser
def take_action(self, parsed_args):
@ -229,7 +233,8 @@ class CreateListener(command.ShowOne):
'pools': v2_utils.format_list,
'l7policies': v2_utils.format_list,
'insert_headers': v2_utils.format_hash,
'allowed_cidrs': v2_utils.format_list_flat}
'allowed_cidrs': v2_utils.format_list_flat,
'tags': v2_utils.format_list_flat}
return (rows,
(utils.get_dict_properties(data['listener'],
@ -308,6 +313,9 @@ class ListListener(lister.Lister):
metavar='<project>',
help="List listeners by project ID."
)
_tag.add_tag_filtering_option_to_parser(parser, 'listener')
return parser
def take_action(self, parsed_args):
@ -357,7 +365,8 @@ class ShowListener(command.ShowOne):
'pools': v2_utils.format_list,
'l7policies': v2_utils.format_list,
'insert_headers': v2_utils.format_hash,
'allowed_cidrs': v2_utils.format_list_flat}
'allowed_cidrs': v2_utils.format_list_flat,
'tags': v2_utils.format_list_flat}
return rows, utils.get_dict_properties(data, rows,
formatters=formatters)
@ -519,6 +528,8 @@ class SetListener(command.Command):
"by the listener (can be set multiple times)."
)
_tag.add_tag_option_to_parser_for_set(parser, 'listener')
return parser
def take_action(self, parsed_args):
@ -527,6 +538,10 @@ class SetListener(command.Command):
listener_id = attrs.pop('listener_id')
v2_utils.set_tags_for_set(
self.app.client_manager.load_balancer.listener_show,
listener_id, attrs, clear_tags=parsed_args.no_tag)
body = {'listener': attrs}
self.app.client_manager.load_balancer.listener_set(
@ -648,17 +663,24 @@ class UnsetListener(command.Command):
action='store_true',
help="Clear all ALPN protocols from the listener."
)
_tag.add_tag_option_to_parser_for_unset(parser, 'listener')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
listener_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.listener_list,
'listeners', parsed_args.listener)
v2_utils.set_tags_for_unset(
self.app.client_manager.load_balancer.listener_show,
listener_id, unset_args, clear_tags=parsed_args.all_tag)
body = {'listener': unset_args}
self.app.client_manager.load_balancer.listener_set(


+ 24
- 3
octaviaclient/osc/v2/load_balancer.py View File

@ -17,6 +17,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
@ -132,6 +133,9 @@ class CreateLoadBalancer(command.ShowOne):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_create(
parser, 'load balancer')
return parser
def take_action(self, parsed_args):
@ -159,7 +163,8 @@ class CreateLoadBalancer(command.ShowOne):
formatters = {
'listeners': v2_utils.format_list,
'pools': v2_utils.format_list,
'l7policies': v2_utils.format_list
'l7policies': v2_utils.format_list,
'tags': v2_utils.format_list_flat
}
return (rows,
@ -326,6 +331,8 @@ class ListLoadBalancer(lister.Lister):
help="List load balancers according to their availability zone."
)
_tag.add_tag_filtering_option_to_parser(parser, 'load balancer')
return parser
def take_action(self, parsed_args):
@ -380,7 +387,8 @@ class ShowLoadBalancer(command.ShowOne):
formatters = {
'listeners': v2_utils.format_list,
'pools': v2_utils.format_list,
'l7policies': v2_utils.format_list
'l7policies': v2_utils.format_list,
'tags': v2_utils.format_list_flat
}
return (rows, (utils.get_dict_properties(
@ -433,12 +441,19 @@ class SetLoadBalancer(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_set(parser, 'load balancer')
return parser
def take_action(self, parsed_args):
attrs = v2_utils.get_loadbalancer_attrs(self.app.client_manager,
parsed_args)
lb_id = attrs.pop('loadbalancer_id')
v2_utils.set_tags_for_set(
self.app.client_manager.load_balancer.load_balancer_show,
lb_id, attrs, clear_tags=parsed_args.no_tag)
body = {'loadbalancer': attrs}
self.app.client_manager.load_balancer.load_balancer_set(
@ -484,17 +499,23 @@ class UnsetLoadBalancer(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'load balancer')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
lb_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.load_balancer_list,
'loadbalancers', parsed_args.loadbalancer)
v2_utils.set_tags_for_unset(
self.app.client_manager.load_balancer.load_balancer_show,
lb_id, unset_args, clear_tags=parsed_args.all_tag)
body = {'loadbalancer': unset_args}
self.app.client_manager.load_balancer.load_balancer_set(


+ 35
- 13
octaviaclient/osc/v2/member.py View File

@ -21,6 +21,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -40,16 +41,17 @@ class ListMember(lister.Lister):
help="Pool name or ID to list the members of."
)
_tag.add_tag_filtering_option_to_parser(parser, 'member')
return parser
def take_action(self, parsed_args):
columns = const.MEMBER_COLUMNS
attrs = v2_utils.get_member_attrs(self.app.client_manager, parsed_args)
pool_id = attrs.pop('pool_id')
data = self.app.client_manager.load_balancer.member_list(
pool_id=pool_id)
**attrs)
return (columns,
(utils.get_dict_properties(
@ -97,8 +99,10 @@ class ShowMember(command.ShowOne):
data = self.app.client_manager.load_balancer.member_show(
pool_id=pool_id, member_id=member_id)
formatters = {'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data, rows, formatters={})))
data, rows, formatters=formatters)))
class CreateMember(command.ShowOne):
@ -189,6 +193,9 @@ class CreateMember(command.ShowOne):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_create(
parser, 'member')
return parser
def take_action(self, parsed_args):
@ -218,9 +225,11 @@ class CreateMember(command.ShowOne):
pool_id, data['member']['id']))
}
formatters = {'tags': v2_utils.format_list_flat}
return (rows,
(utils.get_dict_properties(
data['member'], rows, formatters={})))
data['member'], rows, formatters=formatters)))
class SetMember(command.Command):
@ -294,6 +303,8 @@ class SetMember(command.Command):
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_set(parser, 'member')
return parser
def take_action(self, parsed_args):
@ -303,6 +314,14 @@ class SetMember(command.Command):
pool_id = attrs.pop('pool_id')
member_id = attrs.pop('member_id')
member_show = functools.partial(
self.app.client_manager.load_balancer.member_show,
pool_id
)
v2_utils.set_tags_for_set(
member_show, member_id, attrs, clear_tags=parsed_args.no_tag)
post_data = {"member": attrs}
self.app.client_manager.load_balancer.member_set(
@ -312,10 +331,6 @@ class SetMember(command.Command):
)
if parsed_args.wait:
member_show = functools.partial(
self.app.client_manager.load_balancer.member_show,
pool_id
)
v2_utils.wait_for_active(
status_f=member_show,
res_id=member_id
@ -414,32 +429,39 @@ class UnsetMember(command.Command):
action='store_true',
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'member')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
pool_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.pool_list,
'pools', parsed_args.pool)
member_show = functools.partial(
self.app.client_manager.load_balancer.member_show,
pool_id
)
member_dict = {'pool_id': pool_id, 'member_id': parsed_args.member}
member_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.member_list,
'members', member_dict)
v2_utils.set_tags_for_unset(
member_show, member_id, unset_args,
clear_tags=parsed_args.all_tag)
body = {'member': unset_args}
self.app.client_manager.load_balancer.member_set(
pool_id=pool_id, member_id=member_id, json=body)
if parsed_args.wait:
member_show = functools.partial(
self.app.client_manager.load_balancer.member_show,
pool_id
)
v2_utils.wait_for_active(
status_f=member_show,
res_id=member_id


+ 24
- 3
octaviaclient/osc/v2/pool.py View File

@ -19,6 +19,7 @@ from cliff import lister
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from osc_lib.utils import tags as _tag
from oslo_utils import uuidutils
from octaviaclient.osc.v2 import constants as const
@ -146,6 +147,9 @@ class CreatePool(command.ShowOne):
"by the pool (can be set multiple times)."
)
_tag.add_tag_option_to_parser_for_create(
parser, 'pool')
return parser
def take_action(self, parsed_args):
@ -171,7 +175,8 @@ class CreatePool(command.ShowOne):
formatters = {'loadbalancers': v2_utils.format_list,
'members': v2_utils.format_list,
'listeners': v2_utils.format_list,
'session_persistence': v2_utils.format_hash}
'session_persistence': v2_utils.format_hash,
'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data['pool'], rows, formatters=formatters,
@ -222,6 +227,8 @@ class ListPool(lister.Lister):
help="Filter by load balancer (name or ID).",
)
_tag.add_tag_filtering_option_to_parser(parser, 'pool')
return parser
def take_action(self, parsed_args):
@ -271,7 +278,8 @@ class ShowPool(command.ShowOne):
formatters = {'loadbalancers': v2_utils.format_list,
'members': v2_utils.format_list,
'listeners': v2_utils.format_list,
'session_persistence': v2_utils.format_hash}
'session_persistence': v2_utils.format_hash,
'tags': v2_utils.format_list_flat}
return (rows, (utils.get_dict_properties(
data, rows, formatters=formatters,
@ -382,12 +390,18 @@ class SetPool(command.Command):
)
_tag.add_tag_option_to_parser_for_set(parser, 'pool')
return parser
def take_action(self, parsed_args):
attrs = v2_utils.get_pool_attrs(self.app.client_manager, parsed_args)
pool_id = attrs.pop('pool_id')
v2_utils.set_tags_for_set(
self.app.client_manager.load_balancer.pool_show,
pool_id, attrs, clear_tags=parsed_args.no_tag)
body = {'pool': attrs}
self.app.client_manager.load_balancer.pool_set(
@ -458,17 +472,24 @@ class UnsetPool(command.Command):
action='store_true',
help='Wait for action to complete',
)
_tag.add_tag_option_to_parser_for_unset(parser, 'pool')
return parser
def take_action(self, parsed_args):
unset_args = v2_utils.get_unsets(parsed_args)
if not unset_args:
if not unset_args and not parsed_args.all_tag:
return
pool_id = v2_utils.get_resource_id(
self.app.client_manager.load_balancer.pool_list,
'pools', parsed_args.pool)
v2_utils.set_tags_for_unset(
self.app.client_manager.load_balancer.pool_show,
pool_id, unset_args, clear_tags=parsed_args.all_tag)
body = {'pool': unset_args}
self.app.client_manager.load_balancer.pool_set(


+ 44
- 3
octaviaclient/osc/v2/utils.py View File

@ -124,6 +124,16 @@ def get_resource_id(resource, resource_name, name):
raise osc_exc.CommandError(msg) from e
def add_tags_attr_map(attr_map):
tags_attr_map = {
'tags': ('tags', list),
'any_tags': ('tags-any', list),
'not_tags': ('not-tags', list),
'not_any_tags': ('not-tags-any', list),
}
attr_map.update(tags_attr_map)
def get_loadbalancer_attrs(client_manager, parsed_args):
attr_map = {
'name': ('name', str),
@ -174,8 +184,8 @@ def get_loadbalancer_attrs(client_manager, parsed_args):
client_manager.load_balancer.flavor_list
),
'availability_zone': ('availability_zone', str),
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -229,6 +239,7 @@ def get_listener_attrs(client_manager, parsed_args):
'tls_versions': ('tls_versions', list),
'alpn_protocols': ('alpn_protocols', list),
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -277,6 +288,7 @@ def get_pool_attrs(client_manager, parsed_args):
'tls_ciphers': ('tls_ciphers', str),
'tls_versions': ('tls_versions', list),
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -318,6 +330,7 @@ def get_member_attrs(client_manager, parsed_args):
'enable': ('admin_state_up', lambda x: True),
'disable': ('admin_state_up', lambda x: False),
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -357,6 +370,7 @@ def get_l7policy_attrs(client_manager, parsed_args):
'enable': ('admin_state_up', lambda x: True),
'disable': ('admin_state_up', lambda x: False)
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -391,6 +405,7 @@ def get_l7rule_attrs(client_manager, parsed_args):
'enable': ('admin_state_up', lambda x: True),
'disable': ('admin_state_up', lambda x: False)
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -429,6 +444,7 @@ def get_health_monitor_attrs(client_manager, parsed_args):
'http_version': ('http_version', float),
'domain_name': ('domain_name', str)
}
add_tags_attr_map(attr_map)
_attrs = vars(parsed_args)
attrs = _map_attrs(_attrs, attr_map)
@ -604,8 +620,13 @@ def _format_str_if_need_treat_unset(data):
def get_unsets(parsed_args):
return {arg: None for arg, value in vars(parsed_args).items() if
value is True and arg != 'wait'}
unsets = {}
for arg, value in vars(parsed_args).items():
if value and arg == 'tags':
unsets[arg] = value
elif value is True and arg not in ('wait', 'all_tag'):
unsets[arg] = None
return unsets
def wait_for_active(status_f, res_id):
@ -642,3 +663,23 @@ def wait_for_delete(status_f, res_id,
except exceptions.OctaviaClientException as e:
if e.code != 404:
raise
def set_tags_for_set(resource_get, resource_id, attrs, clear_tags=False):
if attrs.get('tags'):
resource = resource_get(resource_id)
tags = set([] if clear_tags else resource['tags'])
tags |= set(attrs['tags'])
attrs['tags'] = list(tags)
elif clear_tags:
attrs['tags'] = []
def set_tags_for_unset(resource_get, resource_id, attrs, clear_tags=False):
if clear_tags:
attrs['tags'] = []
elif attrs.get('tags'):
resource = resource_get(resource_id)
tags = set(resource['tags'])
tags -= set(attrs['tags'])
attrs['tags'] = list(tags)

+ 5
- 2
octaviaclient/tests/unit/osc/v2/constants.py View File

@ -81,7 +81,8 @@ LISTENER_ATTRS = {
"allowed_cidrs": ['192.0.2.0/24', '198.51.100.0/24'],
'tls_ciphers': "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
'tls_versions': ['TLSv1.1', 'TLSv1.2'],
'alpn_protocols': ['h2', 'http/1.1']
'alpn_protocols': ['h2', 'http/1.1'],
"tags": ["foo", "bar"]
}
LOADBALANCER_ATTRS = {
@ -96,6 +97,7 @@ LOADBALANCER_ATTRS = {
"operating_status": "ONLINE",
"provider": "octavia",
"flavor_id": uuidutils.generate_uuid(dashed=True),
"tags": ["foo", "bar"]
}
L7POLICY_ATTRS = {
@ -159,7 +161,8 @@ POOL_ATTRS = {
"crl_container_ref": uuidutils.generate_uuid(),
"tls_enabled": True,
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
"tls_versions": ['TLSv1.1', 'TLSv1.2']
"tls_versions": ['TLSv1.1', 'TLSv1.2'],
"tags": ["foo", "bar"],
}
QUOTA_ATTRS = {


+ 157
- 0
octaviaclient/tests/unit/osc/v2/test_health_monitor.py View File

@ -60,6 +60,62 @@ class TestHealthMonitorList(TestHealthMonitor):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_health_monitor_list_with_tags(self):
arglist = ['--tags', 'foo,bar']
verifylist = [('tags', ['foo', 'bar'])]
expected_attrs = {
'tags': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_health_monitor_list_with_any_tags(self):
arglist = ['--any-tags', 'foo,bar']
verifylist = [('any_tags', ['foo', 'bar'])]
expected_attrs = {
'tags-any': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_health_monitor_list_with_not_tags(self):
arglist = ['--not-tags', 'foo,bar']
verifylist = [('not_tags', ['foo', 'bar'])]
expected_attrs = {
'not-tags': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_health_monitor_list_with_not_any_tags(self):
arglist = ['--not-any-tags', 'foo,bar']
verifylist = [('not_any_tags', ['foo', 'bar'])]
expected_attrs = {
'not-tags-any': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
class TestHealthMonitorDelete(TestHealthMonitor):
@ -189,6 +245,32 @@ class TestHealthMonitorCreate(TestHealthMonitor):
sleep_time=mock.ANY,
status_field='provisioning_status')
@mock.patch('octaviaclient.osc.v2.utils.get_health_monitor_attrs')
def test_health_monitor_create_with_tag(self, mock_attrs):
mock_attrs.return_value = self.hm_info
arglist = ['mock_pool_id',
'--name', self._hm.name,
'--delay', str(self._hm.delay),
'--timeout', str(self._hm.timeout),
'--max-retries', str(self._hm.max_retries),
'--type', self._hm.type.lower(),
'--tag', 'foo']
verifylist = [
('pool', 'mock_pool_id'),
('name', self._hm.name),
('delay', str(self._hm.delay)),
('timeout', str(self._hm.timeout)),
('max_retries', self._hm.max_retries),
('type', self._hm.type),
('tags', ['foo'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_create.assert_called_with(
json={'healthmonitor': self.hm_info})
class TestHealthMonitorShow(TestHealthMonitor):
@ -257,6 +339,43 @@ class TestHealthMonitorSet(TestHealthMonitor):
sleep_time=mock.ANY,
status_field='provisioning_status')
def test_health_monitor_set_tag(self):
self.api_mock.health_monitor_show.return_value = {
'tags': ['foo']
}
arglist = [self._hm.id, '--tag', 'bar']
verifylist = [
('health_monitor', self._hm.id),
('tags', ['bar']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_set.assert_called_once()
kwargs = self.api_mock.health_monitor_set.mock_calls[0][2]
tags = kwargs['json']['healthmonitor']['tags']
self.assertEqual(2, len(tags))
self.assertIn('foo', tags)
self.assertIn('bar', tags)
def test_health_monitor_set_tag_no_tag(self):
self.api_mock.health_monitor_show.return_value = {
'tags': ['foo']
}
arglist = [self._hm.id, '--tag', 'bar', '--no-tag']
verifylist = [
('health_monitor', self._hm.id),
('tags', ['bar']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_set.assert_called_once_with(
self._hm.id,
json={"healthmonitor": {"tags": ['bar']}})
class TestHealthMonitorUnset(TestHealthMonitor):
PARAMETERS = ('name', 'domain_name', 'expected_codes', 'http_method',
@ -350,3 +469,41 @@ class TestHealthMonitorUnset(TestHealthMonitor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_set.assert_not_called()
def test_health_monitor_unset_tag(self):
self.api_mock.health_monitor_set.reset_mock()
self.api_mock.health_monitor_show.return_value = {
'tags': ['foo', 'bar']
}
arglist = [self._hm.id, '--tag', 'foo']
verifylist = [
('health_monitor', self._hm.id),
('tags', ['foo']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_set.assert_called_once_with(
self._hm.id,
json={"healthmonitor": {"tags": ['bar']}})
def test_health_monitor_unset_all_tag(self):
self.api_mock.health_monitor_set.reset_mock()
self.api_mock.health_monitor_show.return_value = {
'tags': ['foo', 'bar']
}
arglist = [self._hm.id, '--all-tag']
verifylist = [
('health_monitor', self._hm.id),
('all_tag', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.health_monitor_set.assert_called_once_with(
self._hm.id,
json={"healthmonitor": {"tags": []}})

+ 168
- 0
octaviaclient/tests/unit/osc/v2/test_l7policy.py View File

@ -76,6 +76,62 @@ class TestL7PolicyList(TestL7Policy):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_l7policy_list_with_tags(self):
arglist = ['--tags', 'foo,bar']
verifylist = [('tags', ['foo', 'bar'])]
expected_attrs = {
'tags': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.l7policy_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_l7policy_list_with_any_tags(self):
arglist = ['--any-tags', 'foo,bar']
verifylist = [('any_tags', ['foo', 'bar'])]
expected_attrs = {
'tags-any': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.l7policy_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_l7policy_list_with_not_tags(self):
arglist = ['--not-tags', 'foo,bar']
verifylist = [('not_tags', ['foo', 'bar'])]
expected_attrs = {
'not-tags': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.l7policy_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_l7policy_list_with_not_any_tags(self):
arglist = ['--not-any-tags', 'foo,bar']
verifylist = [('not_any_tags', ['foo', 'bar'])]
expected_attrs = {
'not-tags-any': ['foo', 'bar']
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.l7policy_list.assert_called_with(**expected_attrs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
class TestL7PolicyDelete(TestL7Policy):
@ -205,6 +261,40 @@ class TestL7PolicyCreate(TestL7Policy):
sleep_time=mock.ANY,
status_field='provisioning_status')
@mock.patch('octaviaclient.osc.v2.utils.get_l7policy_attrs')
def test_l7policy_create_with_tag(self, mock_attrs):
mock_attrs.return_value = {
'listener_id': self._l7po.listener_id,
'name': self._l7po.name,
'action': 'REDIRECT_TO_POOL',
'redirect_pool_id': self._l7po.redirect_pool_id,
'tags': ['foo']
}
arglist = ['mock_li_id',
'--name', self._l7po.name,
'--action', 'REDIRECT_TO_POOL'.lower(),
'--redirect-pool', self._l7po.redirect_pool_id,
'--tag', 'foo']
verifylist = [
('listener', 'mock_li_id'),
('name', self._l7po.name),
('action', 'REDIRECT_TO_POOL'),
('redirect_pool', self._l7po.redirect_pool_id),
('tags', ['foo'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.l7policy_create.assert_called_with(
json={'l7policy': {
'listener_id': self._l7po.listener_id,
'name': self._l7po.name,
'action': 'REDIRECT_TO_POOL',
'redirect_pool_id': self._l7po.redirect_pool_id,
'tags': ['foo']}})
class TestL7PolicyShow(TestL7Policy):
@ -268,6 +358,44 @@ class TestL7PolicySet(TestL7Policy):
sleep_time=mock.ANY,
status_field='provisioning_status')
def test_l7policy_set_tag(self):
self.api_mock.l7policy_show.return_value = {
'tags': ['foo']
}
arglist = [self._l7po.id, '--tag', 'bar']
verifylist = [
('l7policy', self._l7po.id),
('tags', ['bar']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.l7policy_set.assert_called_once()
kwargs = self.api_mock.l7policy_set.mock_calls[0][2]
tags = kwargs['json']['l7policy']['tags']
self.assertEqual(2, len(tags))
self.assertIn('foo', tags)
self.assertIn('bar', tags)
def test_l7policy_set_tag_no_tag(self):
self.api_mock.l7policy_show.return_value = {
'tags': ['foo']
}
arglist = [self._l7po.id, '--tag', 'bar', '--no-tag']
verifylist = [
('l7policy', self._l7po.id),
('tags', ['bar']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.l7policy_set.assert_called_once_with(
self._l7po.id,
json={'l7policy': {'tags': ['bar']}}
)
class TestL7PolicyUnset(TestL7Policy):
PARAMETERS = ('name', 'description', 'redirect_http_code')
@ -348,3 +476,43 @@ class TestL7PolicyUnset(TestL7Policy):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.l7policy_set.assert_not_called()
def test_l7policy_unset_tag(self):
self.api_mock.l7policy_set.reset_mock()
self.api_mock.l7policy_show.return_value = {
'tags': ['foo', 'bar']
}
arglist = [self._l7po.id, '--tag', 'foo']
verifylist = [
('l7policy', self._l7po.id),
('tags', ['foo']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.api_mock.l7policy_set.assert_called_once_with(
self._l7po.id,
json={'l7policy': {'tags': ['bar']}}
)
<