Implement NeutronV2 Driver

This patch adds a neutronv2 driver. This driver allows one to write policy
against neutron and exposes an improved schema from the first version of the
driver (i.e no more subtables). This patch also exposes security-group-rules
from neutron which were not exposed in the previous driver.

Note: This patch does not remove the original datasource driver. I plan to
remove this in a follow up patchset. The reason why I didn't remove it in this
one is that test_congress uses the neutron datasource driver for several of
its tests.

Implements blueprint: neutron-datasource-driver-refactor
Change-Id: I2124e449d524eea3ab293e0a592b9850f0896bd1
This commit is contained in:
Aaron Rosen 2014-12-10 17:57:39 -08:00
parent 0f55d6dc48
commit d6f4105f01
6 changed files with 1101 additions and 13 deletions

View File

@ -0,0 +1,300 @@
#!/usr/bin/env python
# Copyright (c) 2014 VMware, Inc. All rights reserved.
#
# 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 neutronclient.v2_0.client
from congress.datasources.datasource_driver import DataSourceDriver
from congress.datasources import datasource_utils
from congress.openstack.common import log as logging
LOG = logging.getLogger(__name__)
def d6service(name, keys, inbox, datapath, args):
"""This method is called by d6cage to create a dataservice instance."""
return NeutronV2Driver(name, keys, inbox, datapath, args)
class NeutronV2Driver(DataSourceDriver):
# This is the most common per-value translator, so define it once here.
value_trans = {'translation-type': 'VALUE'}
networks_translator = {
'translation-type': 'HDICT',
'table-name': 'networks',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'name', 'translator': value_trans},
{'fieldname': 'status', 'translator': value_trans},
{'fieldname': 'admin_state_up', 'translator': value_trans},
{'fieldname': 'shared', 'translator': value_trans})}
ports_fixed_ips_translator = {
'translation-type': 'HDICT',
'table-name': 'fixed_ips',
'parent-key': 'id',
'parent-col-name': 'port_id',
'selector-type': 'DICT_SELECTOR',
'in-list': True,
'field-translators':
({'fieldname': 'ip_address', 'translator': value_trans},
{'fieldname': 'subnet_id', 'translator': value_trans})}
ports_security_groups_translator = {
'translation-type': 'LIST',
'table-name': 'security_group_port_bindings',
'parent-key': 'id',
'parent-col-name': 'port_id',
'val-col': 'security_group_id',
'translator': value_trans}
ports_translator = {
'translation-type': 'HDICT',
'table-name': 'ports',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'name', 'translator': value_trans},
{'fieldname': 'network_id', 'translator': value_trans},
{'fieldname': 'mac_address', 'translator': value_trans},
{'fieldname': 'admin_state_up', 'translator': value_trans},
{'fieldname': 'status', 'translator': value_trans},
{'fieldname': 'device_id', 'translator': value_trans},
{'fieldname': 'device_owner', 'translator': value_trans},
{'fieldname': 'fixed_ips',
'translator': ports_fixed_ips_translator},
{'fieldname': 'security_groups',
'translator': ports_security_groups_translator})}
subnets_allocation_pools_translator = {
'translation-type': 'HDICT',
'table-name': 'allocation_pools',
'parent-key': 'id',
'selector-type': 'DICT_SELECTOR',
'in-list': True,
'field-translators':
({'fieldname': 'start', 'translator': value_trans},
{'fieldname': 'end', 'translator': value_trans})}
subnets_dns_nameservers_translator = {
'translation-type': 'LIST',
'table-name': 'dns_nameservers',
'parent-key': 'id',
'parent-col-name': 'subnet_id',
'val-col': 'dns_nameserver',
'translator': value_trans}
subnets_routes_translator = {
'translation-type': 'HDICT',
'table-name': 'host_routes',
'parent-key': 'id',
'parent-col-name': 'subnet_id',
'selector-type': 'DICT_SELECTOR',
'in-list': True,
'field-translators':
({'fieldname': 'destination', 'translator': value_trans},
{'fieldname': 'nexthop', 'translator': value_trans})}
subnets_translator = {
'translation-type': 'HDICT',
'table-name': 'subnets',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'name', 'translator': value_trans},
{'fieldname': 'network_id', 'translator': value_trans},
{'fieldname': 'ip_version', 'translator': value_trans},
{'fieldname': 'cidr', 'translator': value_trans},
{'fieldname': 'gateway_ip', 'translator': value_trans},
{'fieldname': 'enable_dhcp', 'translator': value_trans},
{'fieldname': 'ipv6_ra_mode', 'translator': value_trans},
{'fieldname': 'ipv6_address_mode', 'translator': value_trans},
{'fieldname': 'allocation_pools',
'translator': subnets_allocation_pools_translator},
{'fieldname': 'dns_nameservers',
'translator': subnets_dns_nameservers_translator},
{'fieldname': 'host_routes',
'translator': subnets_routes_translator})}
external_fixed_ips_translator = {
'translation-type': 'HDICT',
'table-name': 'external_fixed_ips',
'parent-key': 'router_id',
'parent-col-name': 'router_id',
'selector-type': 'DICT_SELECTOR',
'in-list': True,
'field-translators':
({'fieldname': 'subnet_id', 'translator': value_trans},
{'fieldname': 'ip_address', 'translator': value_trans})}
routers_external_gateway_infos_translator = {
'translation-type': 'HDICT',
'table-name': 'external_gateway_infos',
'parent-key': 'id',
'parent-col-name': 'router_id',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'network_id', 'translator': value_trans},
{'fieldname': 'enable_snat', 'translator': value_trans},
{'fieldname': 'external_fixed_ips',
'translator': external_fixed_ips_translator})}
routers_translator = {
'translation-type': 'HDICT',
'table-name': 'routers',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'status', 'translator': value_trans},
{'fieldname': 'admin_state_up', 'translator': value_trans},
{'fieldname': 'name', 'translator': value_trans},
{'fieldname': 'distributed', 'translator': value_trans},
{'fieldname': 'external_gateway_info',
'translator': routers_external_gateway_infos_translator})}
security_group_rules_translator = {
'translation-type': 'HDICT',
'table-name': 'security_group_rules',
'parent-key': 'id',
'parent-col-name': 'security_group_id',
'selector-type': 'DICT_SELECTOR',
'in-list': True,
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'remote_group_id', 'translator': value_trans},
{'fieldname': 'direction', 'translator': value_trans},
{'fieldname': 'ethertype', 'translator': value_trans},
{'fieldname': 'protocol', 'translator': value_trans},
{'fieldname': 'port_range_min', 'translator': value_trans},
{'fieldname': 'port_range_max', 'translator': value_trans},
{'fieldname': 'remote_ip_prefix', 'translator': value_trans})}
security_group_translator = {
'translation-type': 'HDICT',
'table-name': 'security_groups',
'selector-type': 'DICT_SELECTOR',
'field-translators':
({'fieldname': 'id', 'translator': value_trans},
{'fieldname': 'tenant_id', 'translator': value_trans},
{'fieldname': 'name', 'translator': value_trans},
{'fieldname': 'description', 'translator': value_trans},
{'fieldname': 'security_group_rules',
'translator': security_group_rules_translator})}
def __init__(self, name='', keys='', inbox=None,
datapath=None, args=None):
super(NeutronV2Driver, self).__init__(name, keys, inbox,
datapath, args)
self.creds = datasource_utils.get_credentials(name, args)
self.register_translator(NeutronV2Driver.networks_translator)
self.register_translator(NeutronV2Driver.ports_translator)
self.register_translator(NeutronV2Driver.subnets_translator)
self.register_translator(NeutronV2Driver.routers_translator)
self.register_translator(NeutronV2Driver.security_group_translator)
self.neutron = neutronclient.v2_0.client.Client(**self.creds)
# Store raw state (result of API calls) so that we can
# avoid re-translating and re-sending if no changes occurred.
# Because translation is not deterministic (we're generating
# UUIDs), it's hard to tell if no changes occurred
# after performing the translation.
self.raw_state = {}
self.initialized = True
def update_from_datasource(self):
LOG.debug("Neutron grabbing networks")
networks = self.neutron.list_networks()
if ('networks' not in self.raw_state or
networks != self.raw_state['networks']):
self.raw_state['networks'] = networks
self._translate_networks(networks)
LOG.debug("Neutron grabbing ports")
ports = self.neutron.list_ports()
if 'ports' not in self.raw_state or ports != self.raw_state['ports']:
self.raw_state['ports'] = ports
self._translate_ports(ports)
subnets = self.neutron.list_subnets()
if ('subnets' not in self.raw_state
or subnets != self.raw_state['subnets']):
self.raw_state['subnets'] = subnets
self._translate_subnets(subnets)
routers = self.neutron.list_routers()
if ('routers' not in self.raw_state
or routers != self.raw_state['routers']):
self.raw_state['routers'] = routers
self._translate_routers(routers)
security_groups = self.neutron.list_security_groups()
if ('security_groups' not in self.raw_state
or security_groups != self.raw_state['security_groups']):
self.raw_state['security_groups'] = security_groups
self._translate_security_groups(security_groups)
def _translate_networks(self, obj):
LOG.debug("networks: %s", dict(obj))
row_data = NeutronV2Driver.convert_objs(obj['networks'],
self.networks_translator)
self.state['networks'] = set()
for table, row in row_data:
self.state[table].add(row)
def _translate_ports(self, obj):
LOG.debug("ports: %s", obj)
row_data = NeutronV2Driver.convert_objs(obj['ports'],
self.ports_translator)
self.state['ports'] = set()
self.state['fixed_ips'] = set()
self.state['security_group_port_bindings'] = set()
for table, row in row_data:
self.state[table].add(row)
def _translate_subnets(self, obj):
LOG.debug("subnets: %s", obj)
row_data = NeutronV2Driver.convert_objs(obj['subnets'],
self.subnets_translator)
self.state['subnets'] = set()
self.state['host_routes'] = set()
self.state['dns_nameservers'] = set()
self.state['allocation_pools'] = set()
for table, row in row_data:
self.state[table].add(row)
def _translate_routers(self, obj):
LOG.debug("routers: %s", obj)
row_data = NeutronV2Driver.convert_objs(obj['routers'],
self.routers_translator)
self.state['routers'] = set()
self.state['external_gateway_infos'] = set()
for table, row in row_data:
self.state[table].add(row)
def _translate_security_groups(self, obj):
LOG.debug("security_groups: %s", obj)
row_data = NeutronV2Driver.convert_objs(obj['security_groups'],
self.security_group_translator)
self.state['security_groups'] = set()
self.state['security_group_rules'] = set()
for table, row in row_data:
self.state[table].add(row)

View File

@ -0,0 +1,431 @@
# Copyright (c) 2013 VMware, Inc. All rights reserved.
#
# 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 contextlib
import mock
from congress.datasources import neutronv2_driver
from congress.tests import base
from congress.tests import helper
class TestNeutronV2Driver(base.TestCase):
def setUp(self):
super(TestNeutronV2Driver, self).setUp()
self.neutron_client_p = mock.patch(
"neutronclient.v2_0.client.Client")
self.neutron_client_p.start()
args = helper.datasource_openstack_args()
args['poll_time'] = 0
args['client'] = mock.MagicMock()
self.driver = neutronv2_driver.NeutronV2Driver(args=args)
self.mock_networks = {'networks': [
{u'admin_state_up': True,
u'id': u'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
u'name': u'private',
u'router:external': False,
u'shared': False,
u'status': u'ACTIVE',
u'subnets': [u'3c0eb3a3-4d16-4b1b-b327-44417182d0bb'],
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'admin_state_up': True,
u'id': u'ecdea1af-7197-43c8-b3b0-34d90f72a2a8',
u'name': u'public',
u'router:external': True,
u'shared': False,
u'status': u'ACTIVE',
u'subnets': [u'10d20df9-e8ba-4756-ba30-d573ceb2e99a'],
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'}]}
self.mock_ports = {'ports': [
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:host_id': None,
u'binding:vif_details': {u'port_filter': True},
u'binding:vif_type': u'ovs',
u'binding:vnic_type': u'normal',
u'device_id': u'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
u'device_owner': u'network:router_gateway',
u'fixed_ips': [
{u'ip_address': u'1.1.1.2',
u'subnet_id': u'10d20df9-e8ba-4756-ba30-d573ceb2e99a'}],
u'id': u'04627c85-3553-436c-a7c5-0a64f5b87bb9',
u'mac_address': u'fa:16:3e:f3:19:e5',
u'name': u'',
u'network_id': u'ecdea1af-7197-43c8-b3b0-34d90f72a2a8',
u'port_security_enabled': False,
u'security_groups': [],
u'status': u'DOWN',
u'tenant_id': u''},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:host_id': None,
u'binding:vif_details': {u'port_filter': True},
u'binding:vif_type': u'ovs',
u'binding:vnic_type': u'normal',
u'device_id': u'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
u'device_owner': u'network:router_interface',
u'fixed_ips': [
{u'ip_address': u'169.254.169.253',
u'subnet_id': u'aa9ad4f7-baf0-4a41-85c3-1cc8a3066db6'}],
u'id': u'87f8933a-9582-48d8-ad16-9abf6e545002',
u'mac_address': u'fa:16:3e:b7:78:e8',
u'name': u'',
u'network_id': u'6743ff85-2cfd-48a7-9d3f-472cd418783e',
u'port_security_enabled': False,
u'security_groups': [],
u'status': u'DOWN',
u'tenant_id': u''},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:host_id': None,
u'binding:vif_details': {u'port_filter': True},
u'binding:vif_type': u'ovs',
u'binding:vnic_type': u'normal',
u'device_id': u'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
u'device_owner': u'network:router_interface',
u'fixed_ips': [
{u'ip_address': u'10.0.0.1',
u'subnet_id': u'3c0eb3a3-4d16-4b1b-b327-44417182d0bb'}],
u'id': u'c58c3246-6c2e-490a-b4d9-3b8d5191b465',
u'mac_address': u'fa:16:3e:08:31:6e',
u'name': u'',
u'network_id': u'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
u'port_security_enabled': False,
u'security_groups': [],
u'status': u'DOWN',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:host_id': None,
u'binding:vif_details': {u'port_filter': True},
u'binding:vif_type': u'ovs',
u'binding:vnic_type': u'normal',
u'device_id': u'',
u'device_owner': u'',
u'fixed_ips': [
{u'ip_address': u'10.0.0.2',
u'subnet_id': u'3c0eb3a3-4d16-4b1b-b327-44417182d0bb'}],
u'id': u'eb50003b-a081-4533-92aa-1cbd97f526a8',
u'mac_address': u'fa:16:3e:af:56:fa',
u'name': u'',
u'network_id': u'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
u'port_security_enabled': True,
u'security_groups': [u'e0239062-4243-4798-865f-7055f03786d6'],
u'status': u'DOWN',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'}]}
self.mock_subnets = {'subnets': [
{u'allocation_pools': [{u'end': u'1.1.1.254',
u'start': u'1.1.1.2'}],
u'cidr': u'1.1.1.0/24',
u'dns_nameservers': [],
u'enable_dhcp': True,
u'gateway_ip': u'1.1.1.1',
u'host_routes': [],
u'id': u'10d20df9-e8ba-4756-ba30-d573ceb2e99a',
u'ip_version': 4,
u'ipv6_address_mode': None,
u'ipv6_ra_mode': None,
u'name': u'',
u'network_id': u'ecdea1af-7197-43c8-b3b0-34d90f72a2a8',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'allocation_pools': [{u'end': u'10.0.0.254',
u'start': u'10.0.0.2'}],
u'cidr': u'10.0.0.0/24',
u'dns_nameservers': [u'8.8.8.8'],
u'enable_dhcp': True,
u'gateway_ip': u'10.0.0.1',
u'host_routes': [{u'destination': u'10.10.0.2/32',
u'nexthop': u'10.0.0.1'}],
u'id': u'3c0eb3a3-4d16-4b1b-b327-44417182d0bb',
u'ip_version': 4,
u'ipv6_address_mode': None,
u'ipv6_ra_mode': None,
u'name': u'private-subnet',
u'network_id': u'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'allocation_pools': [{u'end': u'169.254.169.254',
u'start': u'169.254.169.254'}],
u'cidr': u'169.254.169.252/30',
u'dns_nameservers': [],
u'enable_dhcp': True,
u'gateway_ip': u'169.254.169.253',
u'host_routes': [],
u'id': u'aa9ad4f7-baf0-4a41-85c3-1cc8a3066db6',
u'ip_version': 4,
u'ipv6_address_mode': None,
u'ipv6_ra_mode': None,
u'name': u'meta-f42dc4f1-f371-48cc-95be-cf1b97112ab8',
u'network_id': u'6743ff85-2cfd-48a7-9d3f-472cd418783e',
u'tenant_id': u''}]}
self.mock_routers = {'routers': [
{u'admin_state_up': True,
u'distributed': False,
u'external_gateway_info': {
u'enable_snat': True,
u'external_fixed_ips': [
{u'ip_address': u'1.1.1.2',
u'subnet_id': u'10d20df9-e8ba-4756-ba30-d573ceb2e99a'}],
u'network_id': u'ecdea1af-7197-43c8-b3b0-34d90f72a2a8'},
u'id': u'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
u'name': u'myrouter',
u'routes': [],
u'status': u'DOWN',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'}]}
self.mock_security_groups = {'security_groups': [
{u'description': u'Default security group',
u'id': u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'name': u'default',
u'security_group_rules': [
{u'direction': u'egress',
u'ethertype': u'IPv4',
u'id': u'1d943e83-e4e6-472a-9655-f74eb22f3668',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': None,
u'remote_ip_prefix': None,
u'security_group_id':
u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'tenant_id': u''},
{u'direction': u'ingress',
u'ethertype': u'IPv4',
u'id': u'30be5ee1-5b0a-4929-aca5-0c25f1c6b733',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'remote_ip_prefix': None,
u'security_group_id':
u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'tenant_id': u''},
{u'direction': u'ingress',
u'ethertype': u'IPv6',
u'id': u'639995b8-c3ac-44a3-a4f3-c74f9172ad54',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'remote_ip_prefix': None,
u'security_group_id':
u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'tenant_id': u''},
{u'direction': u'egress',
u'ethertype': u'IPv6',
u'id': u'ed7fd9f6-e390-448a-9f5f-8dd4659282f7',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': None,
u'remote_ip_prefix': None,
u'security_group_id':
u'a268fc32-1a59-4154-9a7c-f453ef92560c',
u'tenant_id': u''}],
u'tenant_id': u''},
{u'description': u'Default security group',
u'id': u'e0239062-4243-4798-865f-7055f03786d6',
u'name': u'default',
u'security_group_rules': [
{u'direction': u'ingress',
u'ethertype': u'IPv6',
u'id': u'8a81fecc-ecc7-48ca-bccc-195799667e23',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': u'e0239062-4243-4798-865f-7055f03786d6',
u'remote_ip_prefix': None,
u'security_group_id':
u'e0239062-4243-4798-865f-7055f03786d6',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'direction': u'ingress',
u'ethertype': u'IPv4',
u'id': u'8f4d9e99-1fe8-4816-9f07-c4ecddea9427',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': u'e0239062-4243-4798-865f-7055f03786d6',
u'remote_ip_prefix': None,
u'security_group_id':
u'e0239062-4243-4798-865f-7055f03786d6',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'direction': u'egress',
u'ethertype': u'IPv4',
u'id': u'e70cf243-3389-4f80-82dc-92a3ec1f2d2a',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': None,
u'remote_ip_prefix': None,
u'security_group_id':
u'e0239062-4243-4798-865f-7055f03786d6',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'},
{u'direction': u'egress',
u'ethertype': u'IPv6',
u'id': u'eca1df0f-b222-4208-8f96-8a8024fd6834',
u'port_range_max': None,
u'port_range_min': None,
u'protocol': None,
u'remote_group_id': None,
u'remote_ip_prefix': None,
u'security_group_id':
u'e0239062-4243-4798-865f-7055f03786d6',
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'}],
u'tenant_id': u'feee0a965cc34274917fb753623dd57d'}]}
self.expected_state = {
'subnets': set([
('3c0eb3a3-4d16-4b1b-b327-44417182d0bb',
'feee0a965cc34274917fb753623dd57d', 'private-subnet',
'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d', 4, '10.0.0.0/24',
'10.0.0.1', 'True', 'None', 'None'),
('aa9ad4f7-baf0-4a41-85c3-1cc8a3066db6', '',
'meta-f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'6743ff85-2cfd-48a7-9d3f-472cd418783e', 4,
'169.254.169.252/30',
'169.254.169.253', 'True', 'None', 'None'),
('10d20df9-e8ba-4756-ba30-d573ceb2e99a',
'feee0a965cc34274917fb753623dd57d', '',
'ecdea1af-7197-43c8-b3b0-34d90f72a2a8', 4, '1.1.1.0/24',
'1.1.1.1', 'True', 'None', 'None')]),
'routers':
set([('f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'feee0a965cc34274917fb753623dd57d', 'DOWN', 'True',
'myrouter', 'False')]),
'dns_nameservers':
set([('3c0eb3a3-4d16-4b1b-b327-44417182d0bb', '8.8.8.8')]),
'security_group_rules':
set([('e0239062-4243-4798-865f-7055f03786d6',
'e70cf243-3389-4f80-82dc-92a3ec1f2d2a',
'feee0a965cc34274917fb753623dd57d', 'None', 'egress',
'IPv4', 'None', 'None', 'None', 'None'),
('a268fc32-1a59-4154-9a7c-f453ef92560c',
'ed7fd9f6-e390-448a-9f5f-8dd4659282f7', '', 'None',
'egress', 'IPv6', 'None', 'None', 'None', 'None'),
('a268fc32-1a59-4154-9a7c-f453ef92560c',
'1d943e83-e4e6-472a-9655-f74eb22f3668', '', 'None',
'egress', 'IPv4', 'None', 'None', 'None', 'None'),
('a268fc32-1a59-4154-9a7c-f453ef92560c',
'30be5ee1-5b0a-4929-aca5-0c25f1c6b733', '',
'a268fc32-1a59-4154-9a7c-f453ef92560c', 'ingress',
'IPv4', 'None', 'None', 'None', 'None'),
('e0239062-4243-4798-865f-7055f03786d6',
'8a81fecc-ecc7-48ca-bccc-195799667e23',
'feee0a965cc34274917fb753623dd57d',
'e0239062-4243-4798-865f-7055f03786d6', 'ingress',
'IPv6', 'None', 'None', 'None', 'None'),
('a268fc32-1a59-4154-9a7c-f453ef92560c',
'639995b8-c3ac-44a3-a4f3-c74f9172ad54', '',
'a268fc32-1a59-4154-9a7c-f453ef92560c', 'ingress',
'IPv6', 'None', 'None', 'None', 'None'),
('e0239062-4243-4798-865f-7055f03786d6',
'8f4d9e99-1fe8-4816-9f07-c4ecddea9427',
'feee0a965cc34274917fb753623dd57d',
'e0239062-4243-4798-865f-7055f03786d6',
'ingress', 'IPv4', 'None', 'None', 'None', 'None'),
('e0239062-4243-4798-865f-7055f03786d6',
'eca1df0f-b222-4208-8f96-8a8024fd6834',
'feee0a965cc34274917fb753623dd57d', 'None', 'egress',
'IPv6', 'None', 'None', 'None', 'None')]),
'ports':
set([('c58c3246-6c2e-490a-b4d9-3b8d5191b465',
'feee0a965cc34274917fb753623dd57d', '',
'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
'fa:16:3e:08:31:6e', 'True', 'DOWN',
'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'network:router_interface'),
('87f8933a-9582-48d8-ad16-9abf6e545002', '', '',
'6743ff85-2cfd-48a7-9d3f-472cd418783e',
'fa:16:3e:b7:78:e8', 'True', 'DOWN',
'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'network:router_interface'),
('eb50003b-a081-4533-92aa-1cbd97f526a8',
'feee0a965cc34274917fb753623dd57d', '',
'63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
'fa:16:3e:af:56:fa', 'True', 'DOWN', '', ''),
('04627c85-3553-436c-a7c5-0a64f5b87bb9', '', '',
'ecdea1af-7197-43c8-b3b0-34d90f72a2a8',
'fa:16:3e:f3:19:e5', 'True', 'DOWN',
'f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'network:router_gateway')]),
'allocation_pools':
set([('10d20df9-e8ba-4756-ba30-d573ceb2e99a', '1.1.1.2',
'1.1.1.254'),
('3c0eb3a3-4d16-4b1b-b327-44417182d0bb', '10.0.0.2',
'10.0.0.254'),
('aa9ad4f7-baf0-4a41-85c3-1cc8a3066db6',
'169.254.169.254', '169.254.169.254')]),
'host_routes':
set([('3c0eb3a3-4d16-4b1b-b327-44417182d0bb',
'10.10.0.2/32', '10.0.0.1')]),
'security_group_port_bindings':
set([('eb50003b-a081-4533-92aa-1cbd97f526a8',
'e0239062-4243-4798-865f-7055f03786d6')]),
'external_gateway_infos':
set([('f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'ecdea1af-7197-43c8-b3b0-34d90f72a2a8', 'True')]),
'fixed_ips':
set([('c58c3246-6c2e-490a-b4d9-3b8d5191b465', '10.0.0.1',
'3c0eb3a3-4d16-4b1b-b327-44417182d0bb'),
('eb50003b-a081-4533-92aa-1cbd97f526a8', '10.0.0.2',
'3c0eb3a3-4d16-4b1b-b327-44417182d0bb'),
('87f8933a-9582-48d8-ad16-9abf6e545002',
'169.254.169.253',
'aa9ad4f7-baf0-4a41-85c3-1cc8a3066db6'),
('04627c85-3553-436c-a7c5-0a64f5b87bb9', '1.1.1.2',
'10d20df9-e8ba-4756-ba30-d573ceb2e99a')]),
'networks':
set([('ecdea1af-7197-43c8-b3b0-34d90f72a2a8',
'feee0a965cc34274917fb753623dd57d', 'public',
'ACTIVE', 'True', 'False'),
('63ce8fbb-12e9-4ecd-9b56-1bbf8b51217d',
'feee0a965cc34274917fb753623dd57d', 'private',
'ACTIVE', 'True', 'False')]),
'security_groups':
set([('e0239062-4243-4798-865f-7055f03786d6',
'feee0a965cc34274917fb753623dd57d', 'default',
'Default security group'),
('a268fc32-1a59-4154-9a7c-f453ef92560c', '',
'default', 'Default security group')]),
'external_fixed_ips':
set([('f42dc4f1-f371-48cc-95be-cf1b97112ab8',
'10d20df9-e8ba-4756-ba30-d573ceb2e99a', '1.1.1.2')])}
def test_update_from_datasource(self):
with contextlib.nested(
mock.patch.object(self.driver.neutron,
"list_networks",
return_value=self.mock_networks),
mock.patch.object(self.driver.neutron,
"list_ports",
return_value=self.mock_ports),
mock.patch.object(self.driver.neutron,
"list_subnets",
return_value=self.mock_subnets),
mock.patch.object(self.driver.neutron,
"list_routers",
return_value=self.mock_routers),
mock.patch.object(self.driver.neutron,
"list_security_groups",
return_value=self.mock_security_groups),
) as (list_networks, list_ports, list_subnets, list_routers,
list_security_groups):
self.driver.update_from_datasource()
self.assertEqual(self.driver.state, self.expected_state)

View File

@ -109,7 +109,7 @@ function configure_congress {
fi
rm -rf $CONGRESS_DATASOURCE_FILE
_configure_service neutron neutron
_configure_service neutron neutronv2
_configure_service nova nova
_configure_service key keystone
_configure_service ceilometer ceilometer

View File

@ -0,0 +1,359 @@
# Copyright 2014 OpenStack Foundation
# All Rights Reserved.
#
# 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 tempest import clients
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
from tempest.scenario import manager_congress
from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
class TestNeutronV2Driver(manager_congress.ScenarioPolicyBase):
@classmethod
def check_preconditions(cls):
super(TestNeutronV2Driver, cls).check_preconditions()
if not (CONF.network.tenant_networks_reachable
or CONF.network.public_network_id):
msg = ('Either tenant_networks_reachable must be "true", or '
'public_network_id must be defined.')
cls.enabled = False
raise cls.skipException(msg)
def setUp(cls):
super(TestNeutronV2Driver, cls).setUp()
if not CONF.service_available.neutron:
skip_msg = ("%s skipped as neutron is not available"
% cls.__name__)
raise cls.skipException(skip_msg)
cls.os = clients.Manager(cls.admin_credentials())
cls.neutron_client = cls.os.network_client
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_networks_table(self):
networks = self.neutron_client.list_networks()
network_map = {}
for network in networks['networks']:
network_map[network['id']] = network
network_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'networks')['columns'])
def _check_data():
results = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'networks'))
for row in results['results']:
network_row = network_map[row['data'][0]]
for index in range(len(network_schema)):
if (str(row['data'][index]) !=
str(network_row[network_schema[index]['name']])):
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_ports_tables(self):
ports = self.neutron_client.list_ports()
port_map = {}
for port in ports['ports']:
port_map[port['id']] = port
port_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'ports')['columns'])
port_sec_binding_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'security_group_port_bindings')['columns'])
fixed_ips_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'fixed_ips')['columns'])
def _check_data():
ports = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'ports'))
security_group_port_bindings = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'security_group_port_bindings'))
fixed_ips = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'fixed_ips'))
# Validate ports table
for row in ports['results']:
port_row = port_map[row['data'][0]]
for index in range(len(port_schema)):
if (str(row['data'][index]) !=
str(port_row[port_schema[index]['name']])):
return False
# validate security_group_port_bindings table
for row in security_group_port_bindings['results']:
port_row = port_map[row['data'][0]]
for index in range(len(port_sec_binding_schema)):
row_index = port_sec_binding_schema[index]['name']
# Translate port_id -> id
if row_index == 'port_id':
if (str(row['data'][index]) !=
str(port_row['id'])):
return False
elif row_index == 'security_group_id':
if (str(row['data'][index]) not in
port_row['security_groups']):
return False
# validate fixed_ips
for row in fixed_ips['results']:
port_row = port_map[row['data'][0]]
for index in range(len(fixed_ips_schema)):
row_index = fixed_ips_schema[index]['name']
if row_index in ['subnet_id', 'ip_address']:
if not port_row['fixed_ips']:
continue
for fixed_ip in port_row['fixed_ips']:
if row['data'][index] == fixed_ip[row_index]:
break
else:
# no subnet_id/ip_address match found
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_subnets_tables(self):
subnets = self.neutron_client.list_subnets()
subnet_map = {}
for subnet in subnets['subnets']:
subnet_map[subnet['id']] = subnet
subnet_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'subnets')['columns'])
host_routes_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'host_routes')['columns'])
dns_nameservers_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'dns_nameservers')['columns'])
allocation_pools_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'allocation_pools')['columns'])
def _check_data():
subnets = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'subnets'))
host_routes = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'host_routes'))
dns_nameservers = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'dns_nameservers'))
allocation_pools = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'allocation_pools'))
# Validate subnets table
for row in subnets['results']:
subnet_row = subnet_map[row['data'][0]]
for index in range(len(subnet_schema)):
if (str(row['data'][index]) !=
str(subnet_row[subnet_schema[index]['name']])):
return False
# validate dns_nameservers
for row in dns_nameservers['results']:
subnet_row = subnet_map[row['data'][0]]
for index in range(len(dns_nameservers_schema)):
row_index = dns_nameservers_schema[index]['name']
if row_index in ['dns_nameserver']:
if (row['data'][index]
not in subnet_row['dns_nameservers']):
return False
# validate host_routes
for row in host_routes['results']:
subnet_row = subnet_map[row['data'][0]]
for index in range(len(host_routes_schema)):
row_index = host_routes_schema[index]['name']
if row_index in ['destination', 'nexthop']:
if not subnet_row['host_routes']:
continue
for host_route in subnet_row['host_routes']:
if row['data'][index] == host_route[row_index]:
break
else:
# no destination/nexthop match found
return False
# validate allocation_pools
for row in allocation_pools['results']:
subnet_row = subnet_map[row['data'][0]]
for index in range(len(allocation_pools_schema)):
row_index = allocation_pools_schema[index]['name']
if row_index in ['start', 'end']:
if not subnet_row['allocation_pools']:
continue
for allocation_pool in subnet_row['allocation_pools']:
if (row['data'][index] ==
allocation_pool[row_index]):
break
else:
# no destination/nexthop match found
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_routers_tables(self):
routers = self.neutron_client.list_routers()
router_map = {}
for router in routers['routers']:
router_map[router['id']] = router
router_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'routers')['columns'])
ext_gw_info_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'external_gateway_infos')['columns'])
def _check_data():
routers = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'routers'))
ext_gw_info = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'external_gateway_infos'))
# Validate routers table
for row in routers['results']:
router_row = router_map[row['data'][0]]
for index in range(len(router_schema)):
if (str(row['data'][index]) !=
str(router_row[router_schema[index]['name']])):
return False
# validate external_gateway_infos
for row in ext_gw_info['results']:
router_ext_gw_info = (
router_map[row['data'][0]]['external_gateway_info'])
# populate router_id
router_ext_gw_info['router_id'] = row['data'][0]
for index in range(len(ext_gw_info_schema)):
val = router_ext_gw_info[ext_gw_info_schema[index]['name']]
if (str(row['data'][index]) != str(val)):
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_security_groups_table(self):
security_groups = self.neutron_client.list_security_groups()
security_groups_map = {}
for security_group in security_groups['security_groups']:
security_groups_map[security_group['id']] = security_group
sg_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'security_groups')['columns'])
def _check_data():
security_groups = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'security_groups'))
# Validate security_group table
for row in security_groups['results']:
sg_row = security_groups_map[row['data'][0]]
for index in range(len(sg_schema)):
if (str(row['data'][index]) !=
str(sg_row[sg_schema[index]['name']])):
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")
@test.attr(type='smoke')
@test.services('network')
def test_neutronv2_security_group_rules_table(self):
security_groups = self.neutron_client.list_security_groups()
sgrs_map = {} # security_group_rules
for sg in security_groups['security_groups']:
for sgr in sg['security_group_rules']:
sgrs_map[sgr['id']] = sgr
sgrs_schema = (
self.admin_manager.congress_client.show_datasource_table_schema(
'neutronv2', 'security_group_rules')['columns'])
def _check_data():
security_group_rules = (
self.admin_manager.congress_client.list_datasource_rows(
'neutronv2', 'security_group_rules'))
# Validate security_group_rules table
for row in security_group_rules['results']:
sg_rule_row = sgrs_map[row['data'][1]]
for index in range(len(sgrs_schema)):
if (str(row['data'][index]) !=
str(sg_rule_row[sgrs_schema[index]['name']])):
return False
return True
if not test.call_until_true(func=_check_data,
duration=200, sleep_for=10):
raise exceptions.TimeoutException("Data did not converge in time "
"or failure in server")

View File

@ -45,16 +45,14 @@ class TestPolicyBasicOps(manager_congress.ScenarioPolicyBase):
@test.services('compute', 'network')
def test_policy_basic_op(self):
self._setup_network_and_servers()
body = {"rule": "port_security_group(port, security_group_name) :-"
"neutron:ports(addr_pairs, security_groups, "
"extra_dhcp_opts, binding_cap, status, name, "
"admin_state_up, network_id, tenant_id, binding_vif, "
"device_owner, mac_address, fixed_ips, port, "
"device_id, binding_host_id1), "
"neutron:ports.security_groups(security_groups, "
"security_group_id), neutron:security_groups("
"tenant_id2, security_group_name, desc2, "
"security_group_id)"}
body = {"rule": "port_security_group(id, security_group_name) "
":-neutronv2:ports(id, tenant_id, name, network_id,"
"mac_address, admin_state_up, status, device_id, "
"device_owner),"
"neutronv2:security_group_port_bindings(id, "
"security_group_id), neutronv2:security_groups("
"security_group_id, tenant_id1, security_group_name,"
"description)"}
results = self.admin_manager.congress_client.create_policy_rule(
'classification', body)
rule_id = results['id']

View File

@ -1,5 +1,5 @@
[neutron]
module: datasources/neutron_driver.py
[neutronv2]
module: datasources/neutronv2_driver.py
username: admin
password: password
auth_url: http://127.0.0.1:5000/v2.0