Add neutron subnets and routers

Adds subnets and routers to supported neutron resources, in addition
to the networks and ports already merged. Subnets are configured as
a child plugin to networks; routers are independent.

This adds a listener for router.interface events on the Ports handler
because no separate notifications are received for those events.

Change-Id: If8efa7033bc99bebbea4e5b7564fc9ed150dbb37
Implements: blueprint neutron-subnet-router
This commit is contained in:
Steve McLellan 2016-03-01 16:05:30 -06:00
parent 8adf00a64b
commit 5efb890ca4
18 changed files with 1508 additions and 25 deletions

View File

@ -53,12 +53,26 @@ Plugin: OS::Neutron::Port
[resource_plugin:os_neutron_port]
enabled = true
Plugin: OS::Neutron::Subnet
^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
[resource_plugin:os_neutron_subnet]
enabled = true
Plugin: OS::Neutron::Router
^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
[resource_plugin:os_neutron_router]
enabled = true
Neutron Configuration
=====================
Neutron sends notifications on create/update/delete actions on the
concepts that it implements. Currently Searchlight supports indexing
for networks and ports, with subnets and routers to follow.
for networks, subnets, ports and routers.
neutron.conf
------------
@ -92,6 +106,8 @@ Release Notes
All provider:* properties of networks are exposed to administrators only.
All binding:* properties of ports are also visible only to administrators.
The 'distributed' and 'ha' router properties are available only to
administrators.
Additional properties can be protected similarly with the `admin_only_fields`
under each plugin's configuration section. Glob-like patterns are supported.
@ -103,3 +119,4 @@ For instance::
See: ADMIN_ONLY_FIELDS in:
* searchlight/elasticsearch/plugins/neutron/networks.py
* searchlight/elasticsearch/plugins/neutron/ports.py
* searchlight/elasticsearch/plugins/neutron/routers.py

View File

@ -14,5 +14,7 @@
"resource:OS::Designate::Zone:allow": "",
"resource:OS::Designate::RecordSet:allow": "",
"resource:OS::Neutron::Net:allow": "",
"resource:OS::Neutron::Port:allow": ""
"resource:OS::Neutron::Port:allow": "",
"resource:OS::Neutron::Subnet:allow": "",
"resource:OS::Neutron::Router:allow": ""
}

View File

@ -1,6 +1,7 @@
---
features:
- Adds neutron plugins for networks and ports.
- Adds neutron plugins for networks, subnets, ports
and routers.
issues:
- Neutron resources do not provide dates (created_at
or updated_at). created_at is left empty; updated_at

View File

@ -18,6 +18,14 @@ import copy
from searchlight.elasticsearch.plugins import utils
def _add_dates(entity, updated_at=None):
"""Nothing in neutron appears to have any dates attached to it, or at
least not that we're able to get at.
"""
if not entity.get('updated_at'):
entity['updated_at'] = updated_at or utils.get_now_str()
def serialize_network(network, updated_at=None):
serialized = copy.deepcopy(network)
# TODO(sjmc7): Once subnets are added, look at whether or not to
@ -33,3 +41,16 @@ def serialize_port(port, updated_at=None):
serialized = copy.deepcopy(port)
serialized['updated_at'] = updated_at or utils.get_now_str()
return serialized
def serialize_subnet(subnet):
serialized = copy.deepcopy(subnet)
serialized['project_id'] = serialized['tenant_id']
return serialized
def serialize_router(router, updated_at=None):
serialized = copy.deepcopy(router)
serialized['updated_at'] = updated_at or utils.get_now_str()
serialized['project_id'] = serialized['tenant_id']
return serialized

View File

@ -18,7 +18,11 @@ from oslo_log import log as logging
from searchlight.elasticsearch.plugins import base
from searchlight.elasticsearch.plugins.neutron import serialize_network
from searchlight.elasticsearch.plugins.neutron import serialize_port
from searchlight.elasticsearch.plugins.neutron import serialize_router
from searchlight.elasticsearch.plugins.neutron import serialize_subnet
from searchlight.elasticsearch.plugins import openstack_clients
from searchlight.elasticsearch.plugins import utils
from searchlight import i18n
LOG = logging.getLogger(__name__)
@ -73,7 +77,10 @@ class PortHandler(base.NotificationBase):
return {
'port.create.end': self.create_or_update,
'port.update.end': self.create_or_update,
'port.delete.end': self.delete
'port.delete.end': self.delete,
'router.interface.create': self.create_or_update_from_interface,
'router.interface.delete': self.delete_from_interface
}
def create_or_update(self, payload, timestamp):
@ -98,3 +105,90 @@ class PortHandler(base.NotificationBase):
'Error deleting port %(port_id)s '
'from index. Error: %(exc)s') %
{'port_id': port_id, 'exc': exc})
def create_or_update_from_interface(self, payload, timestamp):
"""Unfortunately there seems to be no notification for ports created
as part of a router interface creation, nor for DHCP ports. This
means we need to go to the API.
"""
port_id = payload['router_interface']['port_id']
LOG.debug("Retrieving port %s from API", port_id)
nc = openstack_clients.get_neutronclient()
port = nc.show_port(port_id)['port']
serialized = serialize_port(
port, updated_at=utils.timestamp_to_isotime(timestamp))
version = self.get_version(serialized, timestamp)
self.index_helper.save_document(serialized, version=version)
def delete_from_interface(self, payload, timestamp):
"""The partner of create_or_update_from_interface. There's no separate
port deletion notification.
"""
port_id = payload['router_interface']['port_id']
delete_payload = {'port_id': port_id}
self.delete(delete_payload, timestamp)
class SubnetHandler(base.NotificationBase):
@classmethod
def _get_notification_exchanges(cls):
return ['neutron']
def get_event_handlers(self):
return {
'subnet.create.end': self.create_or_update,
'subnet.update.end': self.create_or_update,
'subnet.delete.end': self.delete
}
def create_or_update(self, payload, timestamp):
subnet_id = payload['subnet']['id']
LOG.debug("Updating subnet information for %s", subnet_id)
subnet = serialize_subnet(payload['subnet'])
version = self.get_version(subnet, timestamp)
self.index_helper.save_document(subnet, version=version)
def delete(self, payload, timestamp):
subnet_id = payload['subnet_id']
LOG.debug("Deleting subnet information for %s", subnet_id)
try:
self.index_helper.delete_document({'_id': subnet_id})
except Exception as exc:
LOG.error(_LE(
'Error deleting subnet %(subnet_id)s '
'from index: %(exc)s') %
{'subnet_id': subnet_id, 'exc': exc})
class RouterHandler(base.NotificationBase):
@classmethod
def _get_notification_exchanges(cls):
return ['neutron']
def get_event_handlers(self):
return {
'router.create.end': self.create_or_update,
'router.update.end': self.create_or_update,
'router.delete.end': self.delete
}
def create_or_update(self, payload, timestamp):
router_id = payload['router']['id']
LOG.debug("Updating router information for %s", router_id)
router = serialize_router(
payload['router'],
updated_at=utils.timestamp_to_isotime(timestamp))
version = self.get_version(router, timestamp)
self.index_helper.save_document(router, version=version)
def delete(self, payload, timestamp):
router_id = payload['router_id']
LOG.debug("Deleting router information for %s", router_id)
try:
self.index_helper.delete_document({'_id': router_id})
except Exception as exc:
LOG.error(_LE(
'Error deleting router %(router)s '
'from index: %(exc)s') %
{'router': router_id, 'exc': exc})

View File

@ -0,0 +1,117 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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 searchlight.elasticsearch.plugins import base
from searchlight.elasticsearch.plugins.neutron import notification_handlers
from searchlight.elasticsearch.plugins.neutron import serialize_router
from searchlight.elasticsearch.plugins import openstack_clients
class RouterIndex(base.IndexBase):
NotificationHandlerCls = notification_handlers.RouterHandler
ADMIN_ONLY_FIELDS = ['distributed', 'ha']
@classmethod
def get_document_type(self):
return 'OS::Neutron::Router'
def get_mapping(self):
return {
'dynamic': 'false',
'properties': {
'admin_state_up': {'type': 'boolean'},
'availability_zone_hints': {'type': 'string',
'index': 'not_analyzed'},
'availability_zones': {'type': 'string',
'index': 'not_analyzed'},
# Routers don't have created_at
'description': {'type': 'string'},
'distributed': {'type': 'boolean'},
'external_gateway_info': {
'type': 'nested',
'properties': {
'enable_snat': {'type': 'boolean'},
'external_fixed_ips': {
'type': 'nested',
# TODO(sjmc7) Check we can deal with arbitrary
# levels of nesting with facets
'properties': {
'ip_address': {'type': 'string',
'index': 'not_analyzed'},
'subnet_id': {'type': 'string',
'index': 'not_analyzed'},
}
},
'network_id': {'type': 'string',
'index': 'not_analyzed'}
}
},
'ha': {'type': 'boolean'},
'id': {'type': 'string', 'index': 'not_analyzed'},
'name': {
'type': 'string',
'fields': {
'raw': {'type': 'string', 'index': 'not_analyzed'}
}
},
'project_id': {'type': 'string', 'index': 'not_analyzed'},
# TODO(sjmc7) Decide whether to keep these;
# they seem like trouble
'routes': {
'type': 'nested',
'properties': {
'destination': {'type': 'string',
'index': 'not_analyzed'},
'nexthop': {'type': 'string', 'index': 'not_analyzed'},
'action': {'type': 'string', 'index': 'not_analyzed'},
'source': {'type': 'string',
'index': 'not_analyzed'}
}
},
'status': {'type': 'string', 'index': 'not_analyzed'},
'tenant_id': {'type': 'string', 'index': 'not_analyzed'},
'updated_at': {'type': 'date'}
}
}
@property
def admin_only_fields(self):
from_conf = super(RouterIndex, self).admin_only_fields
return from_conf + RouterIndex.ADMIN_ONLY_FIELDS
@property
def facets_with_options(self):
return ('admin_state_up', 'availability_zones', 'status',
'distributed', 'ha', 'external_gateway_info.enable_snat')
@property
def facets_excluded(self):
return {'tenant_id': True, 'distributed': True, 'ha': True,
'project_id': False}
def _get_rbac_field_filters(self, request_context):
return [
{'term': {'tenant_id': request_context.owner}}
]
def get_objects(self):
neutron_client = openstack_clients.get_neutronclient()
for router in neutron_client.list_routers()['routers']:
yield router
def serialize(self, router):
return serialize_router(router)

View File

@ -0,0 +1,120 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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 searchlight.elasticsearch.plugins import base
from searchlight.elasticsearch.plugins.neutron import notification_handlers
from searchlight.elasticsearch.plugins.neutron import serialize_subnet
from searchlight.elasticsearch.plugins import openstack_clients
class SubnetIndex(base.IndexBase):
NotificationHandlerCls = notification_handlers.SubnetHandler
@classmethod
def get_document_type(self):
return 'OS::Neutron::Subnet'
@classmethod
def parent_plugin_type(cls):
return 'OS::Neutron::Net'
def get_parent_id_field(self):
return 'network_id'
def get_mapping(self):
return {
'dynamic': False,
'properties': {
'allocation_pools': {
'type': 'nested',
'properties': {
'start': {'type': 'string', 'index': 'not_analyzed'},
'end': {'type': 'string', 'index': 'not_analyzed'},
}
},
'cidr': {'type': 'string', 'index': 'not_analyzed'},
'created_at': {'type': 'date'},
'description': {'type': 'string'},
'dns_nameservers': {'type': 'string', 'index': 'not_analyzed'},
'enable_dhcp': {'type': 'boolean'},
'gateway_ip': {'type': 'string', 'index': 'not_analyzed'},
'host_routes': {
'type': 'nested',
'properties': {
'destination': {'type': 'string',
'index': 'not_analyzed'},
'next_hop': {'type': 'string',
'index': 'not_analyzed'}
}
},
'id': {'type': 'string', 'index': 'not_analyzed'},
'ip_version': {'type': 'short'},
'ipv6_address_mode': {'type': 'string',
'index': 'not_analyzed'},
'ipv6_ra_mode': {'type': 'string', 'index': 'not_analyzed'},
'name': {
'type': 'string',
'fields': {
'raw': {'type': 'string', 'index': 'not_analyzed'}
}
},
'network_id': {'type': 'string', 'index': 'not_analyzed'},
'project_id': {'type': 'string', 'index': 'not_analyzed'},
'subnetpool_id': {'type': 'string', 'index': 'not_analyzed'},
'tenant_id': {'type': 'string', 'index': 'not_analyzed'},
'updated_at': {'type': 'date'}
}
}
@property
def requires_role_separation(self):
return self.parent_plugin.requires_role_separation
@property
def facets_with_options(self):
return ('ip_version', 'dns_nameservers', 'network_id', 'enable_dhcp',
'ipv6_address_mode', 'ipv6_ra_mode')
@property
def facets_excluded(self):
"""A map of {name: allow_admin} that indicate which
fields should not be offered as facet options, or those that should
only be available to administrators.
"""
return {'tenant_id': True, 'project_id': False}
def _get_rbac_field_filters(self, request_context):
"""Subnets are visible to their owners and if they belong to networks
with the 'shared' property.
"""
return [{
"or": [
{'term': {'tenant_id': request_context.owner}},
{
'has_parent': {
'type': self.parent_plugin_type(),
'query': {'term': {'shared': True}}
}
}
]
}]
def get_objects(self):
neutron_client = openstack_clients.get_neutronclient()
for subnet in neutron_client.list_subnets()['subnets']:
yield subnet
def serialize(self, subnet):
return serialize_subnet(subnet)

View File

@ -374,19 +374,23 @@ class FunctionalTest(test_utils.BaseTestCase):
plugin_classes = {
'glance': {'images': 'ImageIndex', 'metadefs': 'MetadefIndex'},
'nova': {'servers': 'ServerIndex'},
'neutron': {'networks': 'NetworkIndex', 'ports': 'PortIndex'},
'cinder': {'volumes': 'VolumeIndex', 'snapshots': 'SnapshotIndex'},
'neutron': {'networks': 'NetworkIndex', 'ports': 'PortIndex',
'subnets': 'SubnetIndex', 'routers': 'RouterIndex'},
'swift': {'accounts': 'AccountIndex',
'containers': 'ContainerIndex',
'objects': 'ObjectIndex'}
'objects': 'ObjectIndex'},
}
plugins = include_plugins or (
('glance', 'images'), ('glance', 'metadefs'),
('nova', 'servers'),
('cinder', 'volumes'), ('cinder', 'snapshots'),
('neutron', 'networks'), ('neutron', 'ports'),
('neutron', 'subnets'), ('neutron', 'routers'),
('cinder', 'volumes'), ('cinder', 'snapshots'),
('swift', 'accounts'), ('swift', 'containers'),
('swift', 'objects')
('swift', 'objects'),
)
plugins = filter(lambda plugin: plugin not in exclude_plugins, plugins)

View File

@ -0,0 +1,281 @@
{
"router.create.end": {
"event_type": "router.create.end",
"payload": {
"router": {
"status": "ACTIVE",
"external_gateway_info": null,
"availability_zone_hints": [],
"availability_zones": [],
"name": "test-router",
"admin_state_up": true,
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"distributed": false,
"routes": [],
"ha": false,
"id": "0b143748-44d2-4545-9230-864c3abbc786"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "20cd67462bb540b98210f7dd118b7ce2",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-64f15ef0-c172-44da-bb7e-eafac7ebdee0",
"user_domain": null,
"timestamp": "2016-03-13 23:36:25.080945",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:36:25.145439",
"message_id": "0d408f9c-d996-40b5-98fe-738765ae093d"
}
},
"router.update.end": {
"event_type": "router.update.end",
"payload": {
"router": {
"status": "ACTIVE",
"external_gateway_info": null,
"availability_zone_hints": [],
"availability_zones": [],
"name": "renamed router",
"admin_state_up": true,
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"distributed": false,
"routes": [],
"ha": false,
"id": "0b143748-44d2-4545-9230-864c3abbc786"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "32846cb7f8524e13ae0b399e7dd19f03",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-17f689d2-0f35-4eb7-8074-bed59a2151f0",
"user_domain": null,
"timestamp": "2016-03-13 23:38:18.481114",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:38:18.651866",
"message_id": "e90ab0d3-2ba6-476b-b092-af44ecc29532"
}
},
"router.interface.create": {
"event_type": "router.interface.create",
"payload": {
"router_interface": {
"network_id": "60e80dca-302d-4460-8984-9b85dc782bca",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"subnet_id": "4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805",
"subnet_ids": [
"4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805"
],
"port_id": "a5324522-47d3-4547-85df-a01ef6bde4b1",
"id": "0b143748-44d2-4545-9230-864c3abbc786"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "b29f0dd426ca41af90ba36eb022ddbda",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-f9be76a5-dcf1-4296-9add-a06e8105413e",
"user_domain": null,
"timestamp": "2016-03-13 23:42:47.959774",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:42:48.273491",
"message_id": "a90d5a20-5206-42e3-ab9b-08309cf2d96c"
}
},
"router.interface.delete": {
"event_type": "router.interface.delete",
"payload": {
"router_interface": {
"network_id": "60e80dca-302d-4460-8984-9b85dc782bca",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"subnet_id": "4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805",
"subnet_ids": [
"4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805"
],
"port_id": "a5324522-47d3-4547-85df-a01ef6bde4b1",
"id": "0b143748-44d2-4545-9230-864c3abbc786"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "3d9c4d8a583f42ad84cf8f45d647937a",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": false,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-1c3bc795-43b1-4ad8-aa17-b6f2b5c3fe2b",
"user_domain": null,
"timestamp": "2016-03-17 17:29:32.388395",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:42:51.273491",
"message_id": "6d917992-07ef-471e-9720-6c5e3b60f2d4"
}
},
"router.gateway.set": {
"event_type": "router.update.end",
"payload": {
"router": {
"status": "ACTIVE",
"external_gateway_info": {
"network_id": "8891323e-bf5b-48d7-a75e-669af0608538",
"enable_snat": true,
"external_fixed_ips": [
{
"subnet_id": "8cbe9b71-ecf1-4355-b5ba-dee54ec88fa7",
"ip_address": "172.25.0.3"
},
{
"subnet_id": "d7774648-ba81-477a-9329-2acc9a810e50",
"ip_address": "2001:db8::3"
}
]
},
"availability_zone_hints": [],
"availability_zones": [
"nova"
],
"name": "renamed router",
"admin_state_up": true,
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"distributed": false,
"routes": [],
"ha": false,
"id": "0b143748-44d2-4545-9230-864c3abbc786"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "f55ae4544c6040358bad962ac20f725d",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-0f16f883-c413-480a-9d4b-4e79fc7fd476",
"user_domain": null,
"timestamp": "2016-03-13 23:46:39.283489",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:46:39.809556",
"message_id": "7c92b12c-1477-4e87-a76a-024518da354b"
}
},
"router.delete.end": {
"event_type": "router.delete.end",
"payload": {
"router_id": "0b143748-44d2-4545-9230-864c3abbc786"
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "ee110ae55ebc4ca0af695e106745833c",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-adba6854-8bcf-47a5-9710-7a7a7df33a37",
"user_domain": null,
"timestamp": "2016-03-14 00:44:56.857061",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-14 00:44:57.126816",
"message_id": "56428f7d-3f07-4b23-882d-612d8852e422"
}
}
}

View File

@ -0,0 +1,151 @@
{
"subnet.create.end": {
"event_type": "subnet.create.end",
"payload": {
"subnet": {
"name": "",
"enable_dhcp": true,
"network_id": "60e80dca-302d-4460-8984-9b85dc782bca",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"dns_nameservers": [],
"ipv6_ra_mode": null,
"allocation_pools": [
{
"start": "172.45.0.2",
"end": "172.45.255.254"
}
],
"gateway_ip": "172.45.0.1",
"ipv6_address_mode": null,
"ip_version": 4,
"host_routes": [],
"cidr": "172.45.0.0/16",
"id": "4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805",
"subnetpool_id": null,
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:41:32"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "2af46d5627ef422491be4400038b5709",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-8148c629-919d-4fac-b8ff-57220d7f10b3",
"user_domain": null,
"timestamp": "2016-03-13 23:41:32.473691",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:41:32.562400",
"message_id": "7e3277a1-68cb-4114-87bf-a622d0417ac7"
}
},
"subnet.update.end": {
"event_type": "subnet.update.end",
"payload": {
"subnet": {
"name": "Updated subnet",
"enable_dhcp": true,
"network_id": "60e80dca-302d-4460-8984-9b85dc782bca",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"dns_nameservers": [],
"ipv6_ra_mode": null,
"allocation_pools": [
{
"start": "172.45.0.2",
"end": "172.45.255.254"
}
],
"gateway_ip": "172.45.0.1",
"ipv6_address_mode": null,
"ip_version": 4,
"host_routes": [],
"cidr": "172.45.0.0/16",
"id": "4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805",
"subnetpool_id": null,
"updated_at": "2016-03-13T23:42:20",
"created_at": "2016-03-13T23:41:32"
}
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "cc90d47602134401802295b0b109ab26",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-0171556b-f68e-48ff-93a4-9214b286087b",
"user_domain": null,
"timestamp": "2016-03-13 23:42:20.408564",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-13 23:42:20.631587",
"message_id": "cf136c26-9aa6-40ed-80ac-afc468ca09d7"
}
},
"subnet.delete.end": {
"event_type": "subnet.delete.end",
"payload": {
"subnet_id": "4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805"
},
"publisher_id": "network.devstack",
"ctxt": {
"read_only": false,
"domain": null,
"project_name": "admin",
"user_id": "d2ac2752732444adac10fc4911fdac75",
"show_deleted": false,
"roles": [
"admin"
],
"user_identity": "d2ac2752732444adac10fc4911fdac75 75c31cdaa3604b76b7e279de50aec9f0 - - -",
"project_domain": null,
"tenant_name": "admin",
"auth_token": "f4611508539a43a6b3511e6f8bdd8492",
"resource_uuid": null,
"project_id": "75c31cdaa3604b76b7e279de50aec9f0",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"is_admin": true,
"user": "d2ac2752732444adac10fc4911fdac75",
"request_id": "req-0c5ccd33-730d-40ca-86d9-3b76b7dbc568",
"user_domain": null,
"timestamp": "2016-03-14 00:43:17.952733",
"tenant": "75c31cdaa3604b76b7e279de50aec9f0",
"user_name": "admin"
},
"metadata": {
"timestamp": "2016-03-14 00:43:18.143933",
"message_id": "adac0012-c79c-4905-9a24-1777f9675808"
}
}
}

View File

@ -25,7 +25,22 @@
"router:external": false,
"shared": true,
"status": "ACTIVE",
"subnets": ["63785b90-f3d1-4124-b2e9-1e62dd7c8dab"],
"subnets": ["def"],
"tenant_id": "aaaaaabbbbbbccccc555552222255511"
},
{
"admin_state_up": true,
"id": "00000000-4808-4c88-8c3b-000000000000",
"mtu": 0,
"name": "test-not-shared",
"port_security_enabled": true,
"provider:network_type": "vxlan",
"provider:physical_network": null,
"provider:segmentation_id": 1078,
"router:external": false,
"shared": false,
"status": "ACTIVE",
"subnets": ["abc"],
"tenant_id": "aaaaaabbbbbbccccc555552222255511"
},
{

View File

@ -0,0 +1,60 @@
{
"routers": [
{
"admin_state_up": true,
"availability_zone_hints": [],
"availability_zones": [
"nova"
],
"distributed": false,
"external_gateway_info": {
"enable_snat": true,
"external_fixed_ips": [
{
"ip_address": "172.25.0.3",
"subnet_id": "8cbe9b71-ecf1-4355-b5ba-dee54ec88fa7"
},
{
"ip_address": "2001:db8::3",
"subnet_id": "d7774648-ba81-477a-9329-2acc9a810e50"
}
],
"network_id": "8891323e-bf5b-48d7-a75e-669af0608538"
},
"ha": false,
"id": "0b143748-44d2-4545-9230-864c3abbc786",
"name": "renamed router",
"routes": [],
"status": "ACTIVE",
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0"
},
{
"admin_state_up": true,
"availability_zone_hints": [],
"availability_zones": [
"nova"
],
"distributed": true,
"external_gateway_info": {
"enable_snat": true,
"external_fixed_ips": [
{
"ip_address": "172.25.0.2",
"subnet_id": "8cbe9b71-ecf1-4355-b5ba-dee54ec88fa7"
},
{
"ip_address": "2001:db8::1",
"subnet_id": "d7774648-ba81-477a-9329-2acc9a810e50"
}
],
"network_id": "8891323e-bf5b-48d7-a75e-669af0608538"
},
"ha": false,
"id": "324d16fc-c381-4ea4-8f77-feda17cea1d7",
"name": "router1",
"routes": [],
"status": "ACTIVE",
"tenant_id": "1816a16093df465dbc609cf638422a05"
}
]
}

View File

@ -0,0 +1,122 @@
{
"subnets": [
{
"allocation_pools": [
{
"end": "172.25.255.254",
"start": "172.25.0.2"
}
],
"cidr": "172.25.0.0/16",
"dns_nameservers": [],
"enable_dhcp": false,
"gateway_ip": "172.25.0.1",
"host_routes": [],
"id": "8cbe9b71-ecf1-4355-b5ba-dee54ec88fa7",
"ip_version": 4,
"ipv6_address_mode": null,
"ipv6_ra_mode": null,
"name": "public-subnet",
"network_id": "8891323e-bf5b-48d7-a75e-669af0608538",
"subnetpool_id": null,
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:45:32"
},
{
"allocation_pools": [
{
"end": "2001:db8::1",
"start": "2001:db8::1"
},
{
"end": "2001:db8::ffff:ffff:ffff:ffff",
"start": "2001:db8::3"
}
],
"cidr": "2001:db8::/64",
"dns_nameservers": [],
"enable_dhcp": false,
"gateway_ip": "2001:db8::2",
"host_routes": [],
"id": "d7774648-ba81-477a-9329-2acc9a810e50",
"ip_version": 6,
"ipv6_address_mode": null,
"ipv6_ra_mode": null,
"name": "ipv6-public-subnet",
"network_id": "8891323e-bf5b-48d7-a75e-669af0608538",
"subnetpool_id": null,
"tenant_id": "75c31cdaa3604b76b7e279de50aec9f0",
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:45:32"
},
{
"allocation_pools": [
{
"end": "fd2c:ac9d:23d1:0:ffff:ffff:ffff:ffff",
"start": "fd2c:ac9d:23d1::2"
}
],
"cidr": "fd2c:ac9d:23d1::/64",
"dns_nameservers": [],
"enable_dhcp": true,
"gateway_ip": "fd2c:ac9d:23d1::1",
"host_routes": [],
"id": "12e00c04-7699-4f5a-80e7-8b84c65b2ee3",
"ip_version": 6,
"ipv6_address_mode": "slaac",
"ipv6_ra_mode": "slaac",
"name": "ipv6-private-subnet",
"network_id": "60e80dca-302d-4460-8984-9b85dc782bca",
"subnetpool_id": null,
"tenant_id": "1816a16093df465dbc609cf638422a05",
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:45:32"
},
{
"allocation_pools": [
{
"end": "fd2c:ac9d:23d1:0:ffff:ffff:ffff:ffff",
"start": "fd2c:ac9d:23d1::2"
}
],
"cidr": "fd2c:ac9d:23d1::/64",
"dns_nameservers": [],
"enable_dhcp": true,
"gateway_ip": "4444:ac9d:54d1::1",
"host_routes": [],
"id": "fe6b1c72-2560-421d-bd4c-867e545d1234",
"ip_version": 6,
"ipv6_address_mode": "slaac",
"ipv6_ra_mode": "slaac",
"name": "shared-subnet",
"network_id": "deadbeef-4808-4c88-8c3b-deadbeefdead",
"subnetpool_id": null,
"tenant_id": "aaaaaabbbbbbccccc555552222255511",
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:45:32"
},
{
"allocation_pools": [
{
"end": "fd2c:ac9d:23d1:0:ffff:ffff:ffff:ffff",
"start": "fd2c:ac9d:23d1::2"
}
],
"cidr": "fd2c:ac9d:23d1::/64",
"dns_nameservers": [],
"enable_dhcp": true,
"gateway_ip": "4444:ac9d:54d1::1",
"host_routes": [],
"id": "0134324324-0000-aaaa-cccc-543534534",
"ip_version": 6,
"ipv6_address_mode": "slaac",
"ipv6_ra_mode": "slaac",
"name": "not-shared-subnet",
"network_id": "11112222-4808-4c88-8c3b-111122223333",
"subnetpool_id": null,
"tenant_id": "aaaaaabbbbbbccccc555552222255511",
"updated_at": "2016-03-13T23:41:32",
"created_at": "2016-03-13T23:45:32"
}
]}

View File

@ -26,6 +26,16 @@ from searchlight.tests.functional import test_listener
# This is in the load file
TENANT1 = "8eaac046b2c44ab99246cb0850c7f06d"
TENANT2 = "aaaaaabbbbbbccccc555552222255511"
# Subnets and routers use these
TENANT3 = "75c31cdaa3604b76b7e279de50aec9f0"
TENANT4 = "1816a16093df465dbc609cf638422a05"
NETID3 = "8891323e-bf5b-48d7-a75e-669af0608538"
NETID4 = "60e80dca-302d-4460-8984-9b85dc782bca"
SHARED_NET_ID = "deadbeef-4808-4c88-8c3b-deadbeefdead"
_now_str = timeutils.isotime(datetime.datetime.utcnow())
@ -33,8 +43,17 @@ class TestNeutronPlugins(functional.FunctionalTest):
def setUp(self):
super(TestNeutronPlugins, self).setUp()
self.networks_plugin = self.initialized_plugins['OS::Neutron::Net']
self.subnets_plugin = self.initialized_plugins['OS::Neutron::Subnet']
self.routers_plugin = self.initialized_plugins['OS::Neutron::Router']
self.network_objects = self._load_fixture_data('load/networks.json')
self.subnet_objects = self._load_fixture_data('load/subnets.json')
self.subnet_objects = self.subnet_objects['subnets']
self.router_objects = self._load_fixture_data('load/routers.json')
self.router_objects = self.router_objects['routers']
@mock.patch('searchlight.elasticsearch.plugins.utils.get_now_str')
def test_network_rbac_tenant(self, mock_utcnow_str):
mock_utcnow_str.return_value = _now_str
@ -50,10 +69,11 @@ class TestNeutronPlugins(functional.FunctionalTest):
response, json_content = self._search_request(test_api.MATCH_ALL,
TENANT2)
self.assertEqual(200, response.status)
self.assertEqual(2, json_content['hits']['total'])
self.assertEqual(3, json_content['hits']['total'])
hits = json_content['hits']['hits']
expected_names = ['test-shared', 'test-external-router']
expected_names = ['test-shared', 'test-external-router',
'test-not-shared']
actual_names = [hit['_source']['name'] for hit in hits]
self.assertEqual(set(expected_names), set(actual_names))
@ -62,7 +82,7 @@ class TestNeutronPlugins(functional.FunctionalTest):
{"query": {"match_all": {}}, "all_projects": True},
TENANT2, role="admin")
self.assertEqual(200, response.status)
self.assertEqual(3, json_content['hits']['total'])
self.assertEqual(4, json_content['hits']['total'])
@mock.patch('searchlight.elasticsearch.plugins.utils.get_now_str')
def test_network_rbac_shared_external(self, mock_utcnow_str):
@ -91,30 +111,207 @@ class TestNeutronPlugins(functional.FunctionalTest):
self.assertEqual(set(expected_names), set(actual_names))
@mock.patch('searchlight.elasticsearch.plugins.utils.get_now_str')
def test_subnet_rbac(self, mock_utcnow_str):
"""These tests intentionally don't set a parent network so that they're
just testing the tenant id check
"""
mock_utcnow_str.return_value = _now_str
serialized_subnets = [self.subnets_plugin.serialize(subnet)
for subnet in self.subnet_objects
if subnet["tenant_id"] == TENANT3]
self._index(self.subnets_plugin.alias_name_listener,
self.subnets_plugin.get_document_type(),
serialized_subnets,
TENANT3,
parent_id_field="network_id",
role_separation=True)
class TestNeutronListener(test_listener.TestSearchListenerBase):
serialized_subnets = [self.subnets_plugin.serialize(subnet)
for subnet in self.subnet_objects
if subnet["tenant_id"] == TENANT4]
self._index(self.subnets_plugin.alias_name_listener,
self.subnets_plugin.get_document_type(),
serialized_subnets,
TENANT4,
parent_id_field="network_id",
role_separation=True)
response, json_content = self._search_request(
{"query": {"match_all": {}}, "type": "OS::Neutron::Subnet"},
TENANT3)
self.assertEqual(200, response.status)
self.assertEqual(2, json_content['hits']['total'])
self.assertEqual(set([NETID3]),
set(hit["_source"]["network_id"]
for hit in json_content['hits']['hits']))
@mock.patch('searchlight.elasticsearch.plugins.utils.get_now_str')
def test_shared_network_subnet(self, mock_utcnow_str):
mock_utcnow_str.return_value = _now_str
role_sep = self.networks_plugin.requires_role_separation
tenant2_nets = [self.networks_plugin.serialize(net)
for net in self.network_objects
if net['tenant_id'] == TENANT2]
self._index(self.networks_plugin.alias_name_listener,
self.networks_plugin.get_document_type(),
tenant2_nets,
TENANT2,
role_separation=role_sep)
# Index the subnets that belongs to the shared network
tenant2_subnets = [self.subnets_plugin.serialize(subnet)
for subnet in self.subnet_objects
if subnet["tenant_id"] == TENANT2]
self._index(self.subnets_plugin.alias_name_listener,
self.subnets_plugin.get_document_type(),
tenant2_subnets,
TENANT2,
parent_id_field="network_id",
role_separation=True)
# There are now two networks each with one subnet. Both should
# show up for tenant 2 but only one for tenant3
response, json_content = self._search_request(
{"query": {"match_all": {}}, "type": "OS::Neutron::Subnet"},
TENANT2)
self.assertEqual(2, json_content['hits']['total'])
self.assertEqual(
set(['shared-subnet', 'not-shared-subnet']),
set([h['_source']['name'] for h in json_content['hits']['hits']]))
# Tenant 3 can see the shared network's subnet
response, json_content = self._search_request(
{"query": {"match_all": {}}, "type": "OS::Neutron::Subnet"},
TENANT3)
self.assertEqual(1, json_content['hits']['total'])
self.assertEqual(
'shared-subnet',
json_content['hits']['hits'][0]['_source']['name'])
@mock.patch('searchlight.elasticsearch.plugins.utils.get_now_str')
def test_router_rbac(self, mock_utcnow_str):
mock_utcnow_str.return_value = _now_str
serialized_routers = [self.routers_plugin.serialize(router)
for router in self.router_objects
if router["tenant_id"] == TENANT3]
role_sep = self.routers_plugin.requires_role_separation
self.assertTrue(role_sep)
self._index(self.routers_plugin.alias_name_listener,
self.routers_plugin.get_document_type(),
serialized_routers,
TENANT3,
role_separation=role_sep)
serialized_routers = [self.routers_plugin.serialize(router)
for router in self.router_objects
if router["tenant_id"] == TENANT4]
self._index(self.routers_plugin.alias_name_listener,
self.routers_plugin.get_document_type(),
serialized_routers,
TENANT4,
role_separation=role_sep)
query = {"query": {"match_all": {}}, "type": "OS::Neutron::Router"}
response, json_content = self._search_request(query,
TENANT3)
self.assertEqual(200, response.status)
self.assertEqual(1, json_content['hits']['total'])
self.assertEqual('0b143748-44d2-4545-9230-864c3abbc786',
json_content['hits']['hits'][0]['_source']['id'])
response, json_content = self._search_request(query,
TENANT4)
self.assertEqual(200, response.status)
self.assertEqual(1, json_content['hits']['total'])
self.assertEqual('324d16fc-c381-4ea4-8f77-feda17cea1d7',
json_content['hits']['hits'][0]['_source']['id'])
def test_router_fixed_ip(self):
serialized_routers = [self.routers_plugin.serialize(router)
for router in self.router_objects
if router["tenant_id"] == TENANT4]
role_sep = self.routers_plugin.requires_role_separation
self._index(self.routers_plugin.alias_name_listener,
self.routers_plugin.get_document_type(),
serialized_routers,
TENANT4,
role_separation=role_sep)
query = {
"query": {
"nested": {
"path": "external_gateway_info",
"query": {
"term": {"external_gateway_info.enable_snat": True}
}
}
},
"type": "OS::Neutron::Router"
}
response, json_content = self._search_request(query,
TENANT4)
self.assertEqual(200, response.status)
self.assertEqual(1, json_content['hits']['total'])
fixed_ip_path = "external_gateway_info.external_fixed_ips"
ip_addr = "%s.ip_address" % fixed_ip_path
net_id = "external_gateway_info.network_id"
query = {
"query": {
"nested": {
"path": "external_gateway_info",
"query": {
"bool": {
"must": [
{"term": {net_id: NETID3}},
{"nested": {
"path": fixed_ip_path,
"query": {
"term": {ip_addr: "2001:db8::1"}}
}}
]
}
}
}
}
}
response, json_content = self._search_request(query,
TENANT4)
self.assertEqual(200, response.status)
self.assertEqual(1, json_content['hits']['total'])
class TestNeutronListeners(test_listener.TestSearchListenerBase):
def __init__(self, *args, **kwargs):
super(TestNeutronListener, self).__init__(*args, **kwargs)
super(TestNeutronListeners, self).__init__(*args, **kwargs)
self.network_events = self._load_fixture_data('events/networks.json')
self.port_events = self._load_fixture_data('events/ports.json')
self.subnet_events = self._load_fixture_data('events/subnets.json')
self.router_events = self._load_fixture_data('events/routers.json')
def setUp(self):
super(TestNeutronListener, self).setUp()
super(TestNeutronListeners, self).setUp()
self.networks_plugin = self.initialized_plugins['OS::Neutron::Net']
self.ports_plugin = self.initialized_plugins['OS::Neutron::Port']
self.subnets_plugin = self.initialized_plugins['OS::Neutron::Subnet']
self.routers_plugin = self.initialized_plugins['OS::Neutron::Router']
notification_plugins = {
plugin.document_type: test_listener.StevedoreMock(plugin)
for plugin in (self.networks_plugin, self.ports_plugin)}
for plugin in (self.networks_plugin, self.ports_plugin,
self.subnets_plugin, self.routers_plugin)}
self.notification_endpoint = NotificationEndpoint(notification_plugins)
self.index_name = self.networks_plugin.alias_name_listener
self.listener_alias = self.networks_plugin.alias_name_listener
def test_network_create_update_delete(self):
'''Send network.create.end notification event to listener'''
create_event = self.network_events['network.create.end']
self._send_event_to_listener(create_event, self.index_name)
self._send_event_to_listener(create_event, self.listener_alias)
result = self._verify_event_processing(create_event, owner=TENANT1)
verification_keys = ['id', 'status', 'port_security_enabled', 'name']
self._verify_result(create_event, verification_keys, result,
@ -124,20 +321,20 @@ class TestNeutronListener(test_listener.TestSearchListenerBase):
self.assertEqual('2016-01-13T17:39:04Z', hit['updated_at'])
update_event = self.network_events['network.update.end']
self._send_event_to_listener(update_event, self.index_name)
self._send_event_to_listener(update_event, self.listener_alias)
result = self._verify_event_processing(update_event, owner=TENANT1)
verification_keys = ['id', 'status', 'port_security_enabled', 'name']
self._verify_result(update_event, verification_keys, result,
inner_key='network')
delete_event = self.network_events['network.delete.end']
self._send_event_to_listener(delete_event, self.index_name)
self._send_event_to_listener(delete_event, self.listener_alias)
self._verify_event_processing(delete_event, count=0,
owner=TENANT1)
def test_port_create_event(self):
create_event = self.port_events['port.create.end']
self._send_event_to_listener(create_event, self.index_name)
self._send_event_to_listener(create_event, self.listener_alias)
result = self._verify_event_processing(create_event, owner=TENANT1)
verification_keys = ['id', 'status', 'mac_address', 'status']
self._verify_result(create_event, verification_keys, result,
@ -147,7 +344,7 @@ class TestNeutronListener(test_listener.TestSearchListenerBase):
def test_port_rename_event(self):
update_event = self.port_events['port_rename']
self._send_event_to_listener(update_event, self.index_name)
self._send_event_to_listener(update_event, self.listener_alias)
result = self._verify_event_processing(update_event, owner=TENANT1)
verification_keys = ['name']
self._verify_result(update_event, verification_keys, result,
@ -155,21 +352,21 @@ class TestNeutronListener(test_listener.TestSearchListenerBase):
def test_port_attach_detach_events(self):
create_event = self.port_events['port.create.end']
self._send_event_to_listener(create_event, self.index_name)
self._send_event_to_listener(create_event, self.listener_alias)
result = self._verify_event_processing(create_event, owner=TENANT1)
verification_keys = ['device_owner', 'device_id']
self._verify_result(create_event, verification_keys, result,
inner_key='port')
attach_event = self.port_events['port_attach']
self._send_event_to_listener(attach_event, self.index_name)
self._send_event_to_listener(attach_event, self.listener_alias)
result = self._verify_event_processing(attach_event, owner=TENANT1)
verification_keys = ['device_owner', 'device_id']
self._verify_result(attach_event, verification_keys, result,
inner_key='port')
detach_event = self.port_events['port_detach']
self._send_event_to_listener(detach_event, self.index_name)
self._send_event_to_listener(detach_event, self.listener_alias)
result = self._verify_event_processing(attach_event, owner=TENANT1)
verification_keys = ['device_owner', 'device_id']
self._verify_result(detach_event, verification_keys, result,
@ -177,6 +374,98 @@ class TestNeutronListener(test_listener.TestSearchListenerBase):
def test_port_delete_event(self):
delete_event = self.port_events['port.delete.end']
self._send_event_to_listener(delete_event, self.index_name)
self._send_event_to_listener(delete_event, self.listener_alias)
self._verify_event_processing(None, count=0,
owner=TENANT1)
def test_subnet_create_update_delete(self):
create_event = self.subnet_events['subnet.create.end']
self._send_event_to_listener(create_event, self.listener_alias)
result = self._verify_event_processing(create_event, owner=TENANT3)
verification_keys = ['network_id', 'name']
self._verify_result(create_event, verification_keys, result,
inner_key='subnet')
update_event = self.subnet_events['subnet.update.end']
self._send_event_to_listener(update_event, self.listener_alias)
result = self._verify_event_processing(update_event, owner=TENANT3)
verification_keys = ['network_id', 'name']
self._verify_result(update_event, verification_keys, result,
inner_key='subnet')
delete_event = self.subnet_events['subnet.delete.end']
self._send_event_to_listener(delete_event, self.listener_alias)
self._verify_event_processing(delete_event, count=0,
owner=TENANT3)
def test_router_create_update_delete(self):
create_event = self.router_events['router.create.end']
self._send_event_to_listener(create_event, self.listener_alias)
result = self._verify_event_processing(create_event, owner=TENANT3)
verification_keys = ['status', 'name', 'id']
self._verify_result(create_event, verification_keys, result,
inner_key='router')
update_event = self.router_events['router.update.end']
self._send_event_to_listener(update_event, self.listener_alias)
result = self._verify_event_processing(update_event, owner=TENANT3)
verification_keys = ['status', 'name', 'id']
self._verify_result(update_event, verification_keys, result,
inner_key='router')
delete_event = self.router_events['router.delete.end']
self._send_event_to_listener(delete_event, self.listener_alias)
self._verify_event_processing(delete_event, count=0,
owner=TENANT3)
def test_router_interface_create_delete(self):
"""Check that port creation and deletion is registered on interface
creation and deletion events
"""
interface_port = {
u'port': {
u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'9262552b-6e46-41ee-9ede-393d5f65f325',
u'device_owner': u'network:router_interface',
u'dns_name': None,
u'extra_dhcp_opts': [],
u'fixed_ips': [{
u'ip_address': u'172.45.1.1',
u'subnet_id': u'4cd5c1d7-68ec-4e9a-bf16-9fd7571f8805'
}],
u'id': u'a5324522-47d3-4547-85df-a01ef6bde4b1',
u'mac_address': u'fa:16:3e:5f:06:e3',
u'name': u'',
u'network_id': NETID4,
u'port_security_enabled': False,
u'security_groups': [],
u'status': u'ACTIVE',
u'tenant_id': TENANT3
}
}
create_event = self.router_events['router.interface.create']
with mock.patch('neutronclient.v2_0.client.Client.show_port',
return_value=interface_port):
self._send_event_to_listener(create_event, self.listener_alias)
query = {
"type": "OS::Neutron::Port",
"query": {"term": {"id": "a5324522-47d3-4547-85df-a01ef6bde4b1"}}
}
response, json_content = self._search_request(query,
TENANT3)
self.assertEqual(200, response.status)
self.assertEqual(1, json_content['hits']['total'])
hit = json_content['hits']['hits'][0]['_source']
self.assertEqual(NETID4, hit['network_id'])
self.assertEqual('network:router_interface', hit['device_owner'])
delete_event = self.router_events['router.interface.delete']
self._send_event_to_listener(delete_event, self.listener_alias)
response, json_content = self._search_request(query,
TENANT3)
self.assertEqual(0, json_content['hits']['total'])

View File

@ -0,0 +1,98 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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 datetime
from oslo_utils import timeutils
from searchlight.elasticsearch.plugins.neutron import\
routers as routers_plugin
import searchlight.tests.unit.utils as unit_test_utils
import searchlight.tests.utils as test_utils
_now_str = timeutils.isotime(datetime.datetime.utcnow())
USER1 = u'27f4d76b-be62-4e4e-aa33bb11cc55'
ID1 = "813dd936-663e-4e5b-877c-986021b73e2c"
NETID1 = "98dcb60c-59b9-4b3c-bf6a-b8504112e978"
TENANT1 = "8eaac046b2c44ab99246cb0850c7f06d"
def _router_fixture(router_id, tenant_id, name, **kwargs):
fixture = {
u'admin_state_up': True,
u'availability_zone_hints': [],
u'availability_zones': [],
u'external_gateway_info': {
u'enable_snat': True,
u'external_fixed_ips': [{
u'ip_address': u'172.25.0.2',
u'subnet_id': u'8cbe9b71-ecf1-4355-b5ba-dee54ec88fa7'
}, {
u'ip_address': u'2001:db8::1',
u'subnet_id': u'd7774648-ba81-477a-9329-2acc9a810e50'
}],
u'network_id': u'8891323e-bf5b-48d7-a75e-669af0608538'},
u'id': router_id,
u'name': name,
u'routes': [],
u'status': u'ACTIVE',
u'tenant_id': tenant_id}
fixture.update(**kwargs)
return fixture
def _gateway_info(network_id, fixed_ips):
return {
u'enable_snat': True,
u'external_fixed_ips': fixed_ips,
u'network_id': network_id
}
class TestRouterLoaderPlugin(test_utils.BaseTestCase):
def setUp(self):
super(TestRouterLoaderPlugin, self).setUp()
self.plugin = routers_plugin.RouterIndex()
self._create_fixtures()
def _create_fixtures(self):
self.router1 = _router_fixture(router_id=ID1, network_id=NETID1,
tenant_id=TENANT1, name="test-router-1")
self.routers = [self.router1]
def test_admin_only(self):
self.assertEqual(['distributed', 'ha'], self.plugin.admin_only_fields)
def test_document_type(self):
self.assertEqual('OS::Neutron::Router',
self.plugin.get_document_type())
def test_rbac_filter(self):
fake_request = unit_test_utils.get_fake_request(
USER1, TENANT1, '/v1/search', is_admin=False
)
rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context)
self.assertEqual(
[{"term": {"tenant_id": TENANT1}}],
rbac_terms
)
def test_notification_events(self):
handler = self.plugin.get_notification_handler()
self.assertEqual(
set(['router.create.end', 'router.update.end',
'router.delete.end']),
set(handler.get_event_handlers().keys())
)

View File

@ -0,0 +1,87 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# 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 datetime
from oslo_utils import timeutils
from searchlight.elasticsearch.plugins.neutron import\
subnets as subnets_plugin
import searchlight.tests.unit.utils as unit_test_utils
import searchlight.tests.utils as test_utils
_now_str = timeutils.isotime(datetime.datetime.utcnow())
USER1 = u'27f4d76b-be62-4e4e-aa33bb11cc55'
ID1 = "813dd936-663e-4e5b-877c-986021b73e2c"
NETID1 = "98dcb60c-59b9-4b3c-bf6a-b8504112e978"
TENANT1 = "8eaac046b2c44ab99246cb0850c7f06d"
def _subnet_fixture(network_id, tenant_id, subnet_id, name, **kwargs):
fixture = {
"id": subnet_id,
"network_id": network_id,
"name": name,
"tenant_id": tenant_id,
"ip_version": kwargs.pop("ip_version", 4),
"cidr": kwargs.pop("cidr", "192.0.0.1/24")
}
pools = kwargs.get('allocation_pools', [{"start": "192.0.0.2",
"end": "192.254.254.254"}])
fixture['allocation_pools'] = pools
fixture.update(kwargs)
return fixture
class TestSubnetLoaderPlugin(test_utils.BaseTestCase):
def setUp(self):
super(TestSubnetLoaderPlugin, self).setUp()
self.plugin = subnets_plugin.SubnetIndex()
self._create_fixtures()
def _create_fixtures(self):
self.subnet1 = _subnet_fixture(subnet_id=ID1, network_id=NETID1,
tenant_id=TENANT1, name="test-net-1")
self.subnets = [self.subnet1]
def test_document_type(self):
self.assertEqual('OS::Neutron::Subnet',
self.plugin.get_document_type())
def test_rbac_filter(self):
fake_request = unit_test_utils.get_fake_request(
USER1, TENANT1, '/v1/search', is_admin=False
)
rbac_terms = self.plugin._get_rbac_field_filters(fake_request.context)
self.assertEqual(
[{"or": [
{"term": {"tenant_id": TENANT1}},
{
"has_parent": {
"type": "OS::Neutron::Net",
"query": {"term": {"shared": True}}
}
}
]}],
rbac_terms
)
def test_notification_events(self):
handler = self.plugin.get_notification_handler()
self.assertEqual(
set(['subnet.create.end', 'subnet.update.end',
'subnet.delete.end']),
set(handler.get_event_handlers().keys())
)

View File

@ -282,6 +282,8 @@ class TestSearchDeserializer(test_utils.BaseTestCase):
'OS::Nova::Server',
'OS::Neutron::Net',
'OS::Neutron::Port',
'OS::Neutron::Subnet',
'OS::Neutron::Router',
'OS::Cinder::Volume',
'OS::Cinder::Snapshot'
]

View File

@ -32,7 +32,9 @@ searchlight.index_backend =
os_glance_metadef = searchlight.elasticsearch.plugins.glance.metadefs:MetadefIndex
os_nova_server = searchlight.elasticsearch.plugins.nova.servers:ServerIndex
os_neutron_network = searchlight.elasticsearch.plugins.neutron.networks:NetworkIndex
os_neutron_subnet = searchlight.elasticsearch.plugins.neutron.subnets:SubnetIndex
os_neutron_port = searchlight.elasticsearch.plugins.neutron.ports:PortIndex
os_neutron_router = searchlight.elasticsearch.plugins.neutron.routers:RouterIndex
os_designate_recordset = searchlight.elasticsearch.plugins.designate.recordsets:RecordSetIndex
os_designate_zone = searchlight.elasticsearch.plugins.designate.zones:ZoneIndex
os_cinder_volume = searchlight.elasticsearch.plugins.cinder.volumes:VolumeIndex