Merge "Adjust filters on listing availability zones"
This commit is contained in:
commit
156f29aea3
|
@ -19,6 +19,7 @@ from eventlet import greenthread
|
|||
from neutron_lib.agent import constants as agent_consts
|
||||
from neutron_lib.api import converters
|
||||
from neutron_lib.api.definitions import agent as agent_apidef
|
||||
from neutron_lib.api import extensions
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
|
@ -34,6 +35,7 @@ import oslo_messaging
|
|||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
from neutron.agent.common import utils
|
||||
from neutron.api.rpc.callbacks import version_manager
|
||||
|
@ -42,6 +44,7 @@ from neutron.conf.agent.database import agents_db
|
|||
from neutron.db import _model_query as model_query
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.models import agent as agent_model
|
||||
from neutron.extensions import _availability_zone_filter_lib as azfil_ext
|
||||
from neutron.extensions import agent as ext_agent
|
||||
from neutron.extensions import availability_zone as az_ext
|
||||
from neutron.objects import agent as agent_obj
|
||||
|
@ -56,6 +59,22 @@ agents_db.register_db_agents_opts()
|
|||
# version_manager callback
|
||||
DOWNTIME_VERSIONS_RATIO = 2
|
||||
|
||||
RESOURCE_AGENT_TYPE_MAP = {
|
||||
'network': constants.AGENT_TYPE_DHCP,
|
||||
'router': constants.AGENT_TYPE_L3,
|
||||
}
|
||||
|
||||
AZ_ATTRIBUTE_MAP = {
|
||||
'name': {
|
||||
'agent_key': 'availability_zone',
|
||||
'convert_to': lambda x: x,
|
||||
},
|
||||
'resource': {
|
||||
'agent_key': 'agent_type',
|
||||
'convert_to': lambda x: RESOURCE_AGENT_TYPE_MAP.get(x, x),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_availability_zones_by_agent_type(context, agent_type,
|
||||
availability_zones):
|
||||
|
@ -69,9 +88,26 @@ def get_availability_zones_by_agent_type(context, agent_type,
|
|||
class AgentAvailabilityZoneMixin(az_ext.AvailabilityZonePluginBase):
|
||||
"""Mixin class to add availability_zone extension to AgentDbMixin."""
|
||||
|
||||
_is_az_filter_supported = None
|
||||
|
||||
@property
|
||||
def is_az_filter_supported(self):
|
||||
supported = self._is_az_filter_supported
|
||||
if supported is None:
|
||||
supported = False
|
||||
for plugin in directory.get_plugins().values():
|
||||
if extensions.is_extension_supported(plugin, azfil_ext.ALIAS):
|
||||
supported = True
|
||||
break
|
||||
self._is_az_filter_supported = supported
|
||||
|
||||
return self._is_az_filter_supported
|
||||
|
||||
def _list_availability_zones(self, context, filters=None):
|
||||
result = {}
|
||||
filters = filters or {}
|
||||
if self._is_az_filter_supported or self.is_az_filter_supported:
|
||||
filters = self._adjust_az_filters(filters)
|
||||
agents = agent_obj.Agent.get_objects(context, **filters)
|
||||
for agent in agents:
|
||||
if not agent.availability_zone:
|
||||
|
@ -83,21 +119,46 @@ class AgentAvailabilityZoneMixin(az_ext.AvailabilityZonePluginBase):
|
|||
else:
|
||||
continue
|
||||
key = (agent.availability_zone, resource)
|
||||
result[key] = agent.admin_state_up or result.get(key, False)
|
||||
value = agent.admin_state_up or result.get(key, False)
|
||||
result[key] = 'available' if value else 'unavailable'
|
||||
return result
|
||||
|
||||
def _adjust_az_filters(self, filters):
|
||||
# The intersect of sets gets us applicable filter keys (others ignored)
|
||||
common_keys = six.viewkeys(filters) & six.viewkeys(AZ_ATTRIBUTE_MAP)
|
||||
for key in common_keys:
|
||||
filter_key = AZ_ATTRIBUTE_MAP[key]['agent_key']
|
||||
filter_vals = filters.pop(key)
|
||||
if filter_vals:
|
||||
filter_vals = [AZ_ATTRIBUTE_MAP[key]['convert_to'](v)
|
||||
for v in filter_vals]
|
||||
filters.setdefault(filter_key, [])
|
||||
filters[filter_key] += filter_vals
|
||||
return filters
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_availability_zones(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
"""Return a list of availability zones."""
|
||||
# NOTE(hichihara): 'tenant_id' is dummy for policy check.
|
||||
# it is not visible via API.
|
||||
return [{'state': 'available' if v else 'unavailable',
|
||||
'name': k[0], 'resource': k[1],
|
||||
'tenant_id': context.tenant_id}
|
||||
for k, v in self._list_availability_zones(
|
||||
context, filters).items()]
|
||||
if self._is_az_filter_supported or self.is_az_filter_supported:
|
||||
filter_states = filters.pop('state', [])
|
||||
# NOTE(hichihara): 'tenant_id' is dummy for policy check.
|
||||
# it is not visible via API.
|
||||
return [{'state': v,
|
||||
'name': k[0], 'resource': k[1],
|
||||
'tenant_id': context.tenant_id}
|
||||
for k, v in self._list_availability_zones(
|
||||
context, filters).items()
|
||||
if not filter_states or v in filter_states]
|
||||
else:
|
||||
# NOTE(hichihara): 'tenant_id' is dummy for policy check.
|
||||
# it is not visible via API.
|
||||
return [{'state': v,
|
||||
'name': k[0], 'resource': k[1],
|
||||
'tenant_id': context.tenant_id}
|
||||
for k, v in self._list_availability_zones(
|
||||
context, filters).items()]
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def validate_availability_zones(self, context, resource_type,
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
TODO(hongbin): This module should be deleted once neutron-lib containing
|
||||
https://review.openstack.org/#/c/577545/ change is released.
|
||||
"""
|
||||
|
||||
from neutron_lib.api.definitions import availability_zone as az
|
||||
|
||||
|
||||
ALIAS = 'availability_zone_filter'
|
||||
IS_SHIM_EXTENSION = True
|
||||
IS_STANDARD_ATTR_EXTENSION = False
|
||||
NAME = 'Availability Zone Filter Extension'
|
||||
DESCRIPTION = 'Add filter parameters to AvailabilityZone resource'
|
||||
UPDATED_TIMESTAMP = '2018-06-22T10:00:00-00:00'
|
||||
RESOURCE_ATTRIBUTE_MAP = {}
|
||||
SUB_RESOURCE_ATTRIBUTE_MAP = {}
|
||||
ACTION_MAP = {}
|
||||
REQUIRED_EXTENSIONS = [
|
||||
az.ALIAS
|
||||
]
|
||||
OPTIONAL_EXTENSIONS = []
|
||||
ACTION_STATUS = {}
|
|
@ -0,0 +1,18 @@
|
|||
# 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 neutron.extensions import _availability_zone_filter_lib as apidef
|
||||
from neutron_lib.api import extensions
|
||||
|
||||
|
||||
class Availability_zone_filter(extensions.APIExtensionDescriptor):
|
||||
api_definition = apidef
|
|
@ -156,6 +156,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||
"address-scope",
|
||||
"availability_zone",
|
||||
"network_availability_zone",
|
||||
"availability_zone_filter",
|
||||
"default-subnetpools",
|
||||
"subnet-service-types",
|
||||
"ip-substring-filtering",
|
||||
|
|
|
@ -5,6 +5,7 @@ NETWORK_API_EXTENSIONS+=",agent"
|
|||
NETWORK_API_EXTENSIONS+=",allowed-address-pairs"
|
||||
NETWORK_API_EXTENSIONS+=",auto-allocated-topology"
|
||||
NETWORK_API_EXTENSIONS+=",availability_zone"
|
||||
NETWORK_API_EXTENSIONS+=",availability_zone_filter"
|
||||
NETWORK_API_EXTENSIONS+=",binding"
|
||||
NETWORK_API_EXTENSIONS+=",default-subnetpools"
|
||||
NETWORK_API_EXTENSIONS+=",dhcp_agent_scheduler"
|
||||
|
|
|
@ -39,7 +39,8 @@ class AZExtensionManager(object):
|
|||
|
||||
class AZTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
agents_db.AgentDbMixin):
|
||||
supported_extension_aliases = ["agent", "availability_zone"]
|
||||
supported_extension_aliases = ["agent", "availability_zone",
|
||||
"availability_zone_filter"]
|
||||
|
||||
|
||||
class AZTestCommon(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
||||
|
@ -70,17 +71,45 @@ class TestAZAgentCase(AZTestCommon):
|
|||
res = self._list('availability_zones')
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected, azs)
|
||||
# list with filters
|
||||
res = self._list('availability_zones',
|
||||
query_params="availability_zone=nova1")
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected[:1], azs)
|
||||
# not admin case
|
||||
ctx = context.Context('', 'noadmin')
|
||||
res = self._list('availability_zones', neutron_context=ctx)
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected, azs)
|
||||
|
||||
def test_list_availability_zones_with_filter(self):
|
||||
self._register_azs()
|
||||
helpers.set_agent_admin_state(self.agent3['id'], admin_state_up=False)
|
||||
helpers.set_agent_admin_state(self.agent4['id'], admin_state_up=False)
|
||||
expected = [
|
||||
{'name': 'nova1', 'resource': 'network', 'state': 'available'},
|
||||
{'name': 'nova2', 'resource': 'network', 'state': 'available'},
|
||||
{'name': 'nova2', 'resource': 'router', 'state': 'available'},
|
||||
{'name': 'nova3', 'resource': 'router', 'state': 'unavailable'}]
|
||||
res = self._list('availability_zones')
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected, azs)
|
||||
# list with filter of 'name'
|
||||
res = self._list('availability_zones',
|
||||
query_params="name=nova1")
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected[:1], azs)
|
||||
# list with filter of 'resource'
|
||||
res = self._list('availability_zones',
|
||||
query_params="resource=router")
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected[-2:], azs)
|
||||
# list with filter of 'state' as 'available'
|
||||
res = self._list('availability_zones',
|
||||
query_params="state=available")
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected[:3], azs)
|
||||
# list with filter of 'state' as 'unavailable'
|
||||
res = self._list('availability_zones',
|
||||
query_params="state=unavailable")
|
||||
azs = res['availability_zones']
|
||||
self.assertItemsEqual(expected[-1:], azs)
|
||||
|
||||
def test_list_agent_with_az(self):
|
||||
helpers.register_dhcp_agent(host='host1', az='nova1')
|
||||
res = self._list('agents')
|
||||
|
|
Loading…
Reference in New Issue