Merge "Remove external network hooks and auto-added resources"

This commit is contained in:
Jenkins 2016-03-08 20:54:16 +00:00 committed by Gerrit Code Review
commit 060e9178e0
11 changed files with 45 additions and 388 deletions

View File

@ -74,7 +74,8 @@ def get_default_v4_gateway(client, router, networks):
"""Find the IPv4 default gateway for the router.
"""
LOG.debug('networks = %r', networks)
LOG.debug('external interface = %s', router.external_port.mac_address)
if router.external_port:
LOG.debug('external interface = %s', router.external_port.mac_address)
# Now find the subnet that our external IP is on, and return its
# gateway.
@ -122,11 +123,6 @@ def load_provider_rules(path):
def generate_network_config(client, router, management_port, iface_map):
retval = [
common.network_config(
client,
router.external_port,
iface_map[router.external_port.network_id],
EXTERNAL_NET),
common.network_config(
client,
management_port,
@ -135,6 +131,14 @@ def generate_network_config(client, router, management_port, iface_map):
)
]
if router.external_port:
retval.extend([
common.network_config(
client,
router.external_port,
iface_map[router.external_port.network_id],
EXTERNAL_NET)])
retval.extend(
common.network_config(
client,

View File

@ -15,14 +15,12 @@
# under the License.
import collections
import itertools
import socket
import time
import uuid
import netaddr
import six
from neutronclient.v2_0 import client
from neutronclient.common import exceptions as neutron_exc
@ -43,11 +41,8 @@ CONF = cfg.CONF
neutron_opts = [
cfg.StrOpt('management_network_id'),
cfg.StrOpt('external_network_id'),
cfg.StrOpt('management_subnet_id'),
cfg.StrOpt('external_subnet_id'),
cfg.StrOpt('management_prefix', default='fdca:3ba5:a17a:acda::/64'),
cfg.StrOpt('external_prefix', default='172.16.77.0/24'),
cfg.IntOpt('astara_mgt_service_port', default=5000),
cfg.StrOpt('default_instance_flavor', default=1),
cfg.StrOpt('interface_driver',
@ -206,10 +201,13 @@ class Router(object):
@property
def ports(self):
return itertools.chain(
[self.external_port],
self.internal_ports
)
if self.external_port:
return itertools.chain(
[self.external_port],
self.internal_ports
)
else:
return self.internal_ports
class Network(DictModelBase):
@ -753,63 +751,6 @@ class Neutron(object):
for port in port_data:
self.api_client.delete_port(port['id'])
def create_router_external_port(self, router):
# FIXME: Need to make this smarter in case the switch is full.
network_args = {'network_id': self.conf.external_network_id}
update_args = {
'name': router.name,
'admin_state_up': router.admin_state_up,
'external_gateway_info': network_args
}
self.api_client.update_router(
router.id,
body=dict(router=update_args)
)
new_port = self.get_router_external_port(router)
# Make sure the port has enough IPs.
subnets = self.get_network_subnets(self.conf.external_network_id)
sn_by_id = {
sn.id: sn
for sn in subnets
}
sn_by_version = collections.defaultdict(list)
for sn in subnets:
sn_by_version[sn.ip_version].append(sn)
versions_needed = set(sn_by_version.keys())
found = set(sn_by_id[fip.subnet_id].ip_version
for fip in new_port.fixed_ips)
if found != versions_needed:
missing_versions = list(sorted(versions_needed - found))
raise MissingIPAllocation(
new_port.id,
[(mv, [sn.id for sn in sn_by_version[mv]])
for mv in missing_versions]
)
return new_port
def get_router_external_port(self, router):
for i in six.moves.range(self.conf.max_retries):
LOG.debug(
'Looking for router external port. Attempt %d of %d',
i,
cfg.CONF.max_retries,
)
query_dict = {
'device_owner': DEVICE_OWNER_ROUTER_GW,
'device_id': router.id,
'network_id': self.conf.external_network_id
}
ports = self.api_client.list_ports(**query_dict)['ports']
if len(ports):
port = Port.from_dict(ports[0])
LOG.debug('Found router external port: %s', port.id)
return port
time.sleep(self.conf.retry_delay)
raise RouterGatewayMissing()
def _ensure_local_port(self, network_id, subnet_id, prefix,
network_type):
driver = importutils.import_object(self.conf.interface_driver,
@ -900,13 +841,6 @@ class Neutron(object):
driver.init_l3(driver.get_device_name(port), [ip_cidr])
return ip_cidr
def ensure_local_external_port(self):
return self._ensure_local_port(
self.conf.external_network_id,
self.conf.external_subnet_id,
self.conf.external_prefix,
'external')
def ensure_local_service_port(self):
return self._ensure_local_port(
self.conf.management_network_id,

View File

@ -164,21 +164,6 @@ class Router(BaseDriver):
self.log.info(_('Config updated for %s after %s seconds'),
self.name, round(delta, 2))
def pre_plug(self, worker_context):
"""pre-plug hook
Sets up the external port.
:param worker_context:
:returs: None
"""
if self._router.external_port is None:
# FIXME: Need to do some work to pick the right external
# network for a tenant.
self.log.debug('Adding external port to router %s')
ext_port = worker_context.neutron.create_router_external_port(
self._router)
self._router.external_port = ext_port
def make_ports(self, worker_context):
"""make ports call back for the nova client.

View File

@ -46,7 +46,6 @@ MAIN_OPTS = [
cfg.StrOpt('host',
default=socket.getfqdn(),
help="The hostname Astara is running on"),
cfg.BoolOpt('plug_external_port', default=True),
]
CONF.register_opts(MAIN_OPTS)
@ -122,10 +121,6 @@ def main(argv=sys.argv[1:]):
# bring the mgt tap interface up
mgt_ip_address = neutron.ensure_local_service_port().split('/')[0]
# bring the external port
if cfg.CONF.plug_external_port:
neutron.ensure_local_external_port()
# Set up the queue to move messages between the eventlet-based
# listening process and the scheduler.
notification_queue = multiprocessing.Queue()

View File

@ -147,15 +147,17 @@ class TestAstaraClient(unittest.TestCase):
self.assertEqual(result, expected)
expected_calls = [
mock.call(
mock_client, fakes.fake_router.external_port,
'ge1', 'external'),
mock.call(
mock_client, fakes.fake_router.management_port,
'ge0', 'management'),
mock.call(
mock_client, fakes.fake_router.external_port,
'ge1', 'external'),
mock.call(
mock_client, fakes.fake_int_port,
'ge2', 'internal', mock.ANY)]
for c in expected_calls:
self.assertIn(c, mock_net_conf.call_args_list)
mock_net_conf.assert_has_calls(expected_calls)
def test_generate_floating_config(self):

View File

@ -15,8 +15,6 @@
# under the License.
import copy
import mock
import netaddr
@ -364,211 +362,9 @@ class TestNeutronWrapper(base.RugTestBase):
self.assertFalse(neutron_wrapper.api_client.delete_port.called)
class TestExternalPort(base.RugTestBase):
EXTERNAL_NET_ID = 'a0c63b93-2c42-4346-909e-39c690f53ba0'
EXTERNAL_PORT_ID = '089ae859-10ec-453c-b264-6c452fc355e5'
ROUTER = {
u'status': u'ACTIVE',
u'external_gateway_info': {
u'network_id': EXTERNAL_NET_ID,
u'enable_snat': True},
u'name': u'ak-b81e555336da4bf48886e5b93ac6186d',
u'admin_state_up': True,
u'tenant_id': u'b81e555336da4bf48886e5b93ac6186d',
u'ports': [
# This is the external port:
{u'status': u'ACTIVE',
u'binding:host_id': u'devstack-develop',
u'name': u'',
u'allowed_address_pairs': [],
u'admin_state_up': True,
u'network_id': EXTERNAL_NET_ID,
u'tenant_id': u'',
u'extra_dhcp_opts': [],
u'binding:vif_type': u'ovs',
u'device_owner': u'network:router_gateway',
u'binding:capabilities': {u'port_filter': True},
u'mac_address': u'fa:16:3e:a1:a6:ac',
u'fixed_ips': [
{u'subnet_id': u'ipv4snid',
u'ip_address': u'172.16.77.2'},
{u'subnet_id': u'ipv6snid',
u'ip_address': u'fdee:9f85:83be::0'}],
u'id': EXTERNAL_PORT_ID,
u'security_groups': [],
u'device_id': u'7770b189-1223-4d85-9bf7-4d7bc2a28cd7'},
# Some other nice ports you might like:
{u'status': u'ACTIVE',
u'binding:host_id': u'devstack-develop',
u'name': u'',
u'allowed_address_pairs': [],
u'admin_state_up': True,
u'network_id': u'adf190e0-b281-4453-bd87-4ae6fd96d5c1',
u'tenant_id': u'a09298ceed154d26b4ea96977e1c7f17',
u'extra_dhcp_opts': [],
u'binding:vif_type': u'ovs',
u'device_owner': u'network:router_management',
u'binding:capabilities': {u'port_filter': True},
u'mac_address': u'fa:16:3e:e5:dd:55',
u'fixed_ips': [
{u'subnet_id': u'ipv6snid2',
u'ip_address': u'fdca:3ba5:a17a:acda::0'}],
u'id': u'2f4e41b2-c923-48e5-ad19-59e4d02c26a4',
u'security_groups': [],
u'device_id': u'7770b189-1223-4d85-9bf7-4d7bc2a28cd7'},
{u'status': u'ACTIVE',
u'binding:host_id': u'devstack-develop',
u'name': u'',
u'allowed_address_pairs': [],
u'admin_state_up': True,
u'network_id': u'0c04f39c-f739-44dd-9e65-dca6ae20e35c',
u'tenant_id': u'b81e555336da4bf48886e5b93ac6186d',
u'extra_dhcp_opts': [],
u'binding:vif_type': u'ovs',
u'device_owner': u'network:router_interface',
u'binding:capabilities': {u'port_filter': True},
u'mac_address': u'fa:16:3e:e7:27:fc',
u'fixed_ips': [{u'subnet_id': u'ipv4snid2',
u'ip_address': u'192.168.0.1'},
{u'subnet_id': u'ipv6snid3',
u'ip_address': u'fdd6:a1fa:cfa8:cd70::1'}],
u'id': u'b24139b8-a3d0-46cf-bc53-f4b70bb33596',
u'security_groups': [],
u'device_id': u'7770b189-1223-4d85-9bf7-4d7bc2a28cd7'}],
u'routes': [],
u'id': u'5366e8ca-b3e4-408a-91d4-e207af48c755',
}
SUBNETS = [
neutron.Subnet(u'ipv4snid', u'ipv4snid', None, None, 4,
'172.16.77.0/24', '172.16.77.1', False,
[], [], None),
neutron.Subnet(u'ipv6snid', u'ipv4snid', None, None, 6,
'fdee:9f85:83be::/48', 'fdee:9f85:83be::1',
False, [], [], None),
]
def setUp(self):
super(TestExternalPort, self).setUp()
self.conf = mock.Mock()
self.conf.external_network_id = 'ext'
self.conf.max_retries = 3
self.conf.retry_delay = 1
self.conf.external_network_id = self.EXTERNAL_NET_ID
self.router = neutron.Router.from_dict(self.ROUTER)
@mock.patch('astara.api.neutron.AstaraExtClientWrapper')
def test_create(self, client_wrapper):
mock_client = mock.Mock()
mock_client.show_router.return_value = {'router': self.ROUTER}
mock_client.list_ports.return_value = {
'ports': [self.ROUTER['ports'][0]]
}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
with mock.patch.object(neutron_wrapper, 'get_network_subnets') as gns:
gns.return_value = self.SUBNETS
port = neutron_wrapper.create_router_external_port(self.router)
self.assertEqual(port.id, self.EXTERNAL_PORT_ID)
@mock.patch('astara.api.neutron.AstaraExtClientWrapper')
def test_create_missing_gateway_port(self, client_wrapper):
self.conf.retry_delay = 0
mock_client = mock.Mock()
router = copy.deepcopy(self.ROUTER)
router['ports'] = []
mock_client.show_router.return_value = {'router': router}
mock_client.list_ports.return_value = {'ports': []}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
with mock.patch.object(neutron_wrapper, 'get_network_subnets') as gns:
gns.return_value = self.SUBNETS
self.assertRaises(
neutron.RouterGatewayMissing,
neutron_wrapper.create_router_external_port,
self.router
)
@mock.patch('astara.api.neutron.AstaraExtClientWrapper')
def test_missing_v4(self, client_wrapper):
mock_client = mock.Mock()
router = copy.deepcopy(self.ROUTER)
del router['ports'][0]['fixed_ips'][0]
mock_client.list_ports.return_value = {
'ports': [router['ports'][0]]
}
mock_client.show_router.return_value = {'router': router}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
with mock.patch.object(neutron_wrapper, 'get_network_subnets') as gns:
gns.return_value = self.SUBNETS
try:
neutron_wrapper.create_router_external_port(self.router)
except neutron.MissingIPAllocation as e:
self.assertEqual(4, e.missing[0][0])
else:
self.fail('Should have seen MissingIPAllocation')
@mock.patch('astara.api.neutron.AstaraExtClientWrapper')
def test_missing_v6(self, client_wrapper):
mock_client = mock.Mock()
router = copy.deepcopy(self.ROUTER)
del router['ports'][0]['fixed_ips'][1]
mock_client.list_ports.return_value = {
'ports': [router['ports'][0]]
}
mock_client.show_router.return_value = {'router': router}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
with mock.patch.object(neutron_wrapper, 'get_network_subnets') as gns:
gns.return_value = self.SUBNETS
try:
neutron_wrapper.create_router_external_port(self.router)
except neutron.MissingIPAllocation as e:
self.assertEqual(6, e.missing[0][0])
else:
self.fail('Should have seen MissingIPAllocation')
@mock.patch('astara.api.neutron.AstaraExtClientWrapper')
def test_missing_both(self, client_wrapper):
mock_client = mock.Mock()
router = copy.deepcopy(self.ROUTER)
router['ports'][0]['fixed_ips'] = []
mock_client.show_router.return_value = {'router': router}
mock_client.list_ports.return_value = {
'ports': [router['ports'][0]]
}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
with mock.patch.object(neutron_wrapper, 'get_network_subnets') as gns:
gns.return_value = self.SUBNETS
try:
neutron_wrapper.create_router_external_port(self.router)
except neutron.MissingIPAllocation as e:
self.assertEqual(4, e.missing[0][0])
self.assertEqual(6, e.missing[1][0])
else:
self.fail('Should have seen MissingIPAllocation')
class TestLocalServicePorts(base.RugTestBase):
def setUp(self):
super(TestLocalServicePorts, self).setUp()
self.config(external_network_id='fake_extnet_network_id')
self.config(external_subnet_id='fake_extnet_subnet_id')
self.config(external_prefix='172.16.77.0/24')
self.config(management_network_id='fake_mgtnet_network_id')
self.config(management_subnet_id='fake_mgtnet_subnet_id')
self.config(management_prefix='172.16.77.0/24')
@ -579,17 +375,6 @@ class TestLocalServicePorts(base.RugTestBase):
init_l3=mock.Mock(),
get_device_name=mock.Mock())
def test_ensure_local_external_port(self):
with mock.patch.object(self.neutron_wrapper,
'_ensure_local_port') as ep:
self.neutron_wrapper.ensure_local_external_port()
ep.assert_called_with(
'fake_extnet_network_id',
'fake_extnet_subnet_id',
'172.16.77.0/24',
'external',
)
def test_ensure_local_service_port(self):
with mock.patch.object(self.neutron_wrapper,
'_ensure_local_port') as ep:

View File

@ -117,25 +117,6 @@ class RouterDriverTest(base.RugTestBase):
rtr.mgt_port,
'fake_config',)
def test_pre_plug_no_external_port(self):
rtr = self._init_driver()
fake_router_obj = fakes.fake_router()
fake_router_obj.external_port = None
rtr._router = fake_router_obj
self.ctx.neutron.create_router_external_port.return_value = 'fake_port'
rtr.pre_plug(self.ctx)
self.ctx.neutron.create_router_external_port.assert_called_with(
fake_router_obj,
)
self.assertEqual(rtr._router.external_port, 'fake_port')
def test_pre_plug_with_external_port(self):
rtr = self._init_driver()
fake_router_obj = fakes.fake_router()
fake_router_obj.external_port = 'fake_port'
rtr.pre_plug(self.ctx)
self.assertFalse(self.ctx.neutron.create_router_external_port.called)
@mock.patch('astara.drivers.router.Router._ensure_cache')
def test_make_ports(self, mock_ensure_cache):
rtr = self._init_driver()

View File

@ -85,6 +85,7 @@ function configure_astara_neutron() {
# We need the RUG to be able to get neutron's events notification like port.create.start/end
# or router.interface.start/end to make it able to boot astara routers
iniset $NEUTRON_CONF DEFAULT notification_driver "neutron.openstack.common.notifier.rpc_notifier"
iniset $NEUTRON_CONF DEFAULT astara_auto_add_resources False
}
function configure_astara_horizon() {
@ -143,15 +144,6 @@ function create_astara_nova_flavor() {
iniset $ASTARA_CONF router instance_flavor $ROUTER_INSTANCE_FLAVOR_ID
}
function _remove_subnets() {
# Attempt to delete subnets associated with a network.
# We have to modify the output of net-show to allow it to be
# parsed properly as shell variables, and we run both commands in
# a subshell to avoid polluting the local namespace.
(eval $(neutron $auth_args net-show -f shell $1 | sed 's/:/_/g');
neutron $auth_args subnet-delete $subnets || true)
}
function pre_start_astara() {
# Create and init the database
recreate_database astara
@ -163,33 +155,13 @@ function pre_start_astara() {
# CLI.
unset OS_TENANT_NAME OS_PROJECT_NAME
if ! neutron $auth_args net-show $PUBLIC_NETWORK_NAME; then
neutron $auth_args net-create $PUBLIC_NETWORK_NAME --router:external
fi
# Remove the ipv6 subnet created automatically before adding our own.
# NOTE(adam_g): For some reason this fails the first time and needs to be repeated?
_remove_subnets $PUBLIC_NETWORK_NAME ; _remove_subnets $PUBLIC_NETWORK_NAME
typeset public_subnet_id=$(neutron $auth_args subnet-create --ip-version 4 $PUBLIC_NETWORK_NAME 172.16.77.0/24 | grep ' id ' | awk '{ print $4 }')
iniset $ASTARA_CONF DEFAULT external_subnet_id $public_subnet_id
neutron $auth_args subnet-create --ip-version 6 $PUBLIC_NETWORK_NAME fdee:9f85:83be::/48
# setup masq rule for public network
sudo iptables -t nat -A POSTROUTING -s 172.16.77.0/24 -o $PUBLIC_INTERFACE_DEFAULT -j MASQUERADE
neutron $auth_args net-show $PUBLIC_NETWORK_NAME | grep ' id ' | awk '{ print $4 }'
typeset public_network_id=$(neutron $auth_args net-show $PUBLIC_NETWORK_NAME | grep ' id ' | awk '{ print $4 }')
iniset $ASTARA_CONF DEFAULT external_network_id $public_network_id
#sudo iptables -t nat -A POSTROUTING -s 172.16.77.0/24 -o $PUBLIC_INTERFACE_DEFAULT -j MASQUERADE
neutron $auth_args net-create mgt
typeset mgt_network_id=$(neutron $auth_args net-show mgt | grep ' id ' | awk '{ print $4 }')
iniset $ASTARA_CONF DEFAULT management_network_id $mgt_network_id
# Remove the ipv6 subnet created automatically before adding our own.
_remove_subnets mgt
local subnet_create_args=""
if [[ "$ASTARA_MANAGEMENT_PREFIX" =~ ':' ]]; then
subnet_create_args="--ip-version=6 --ipv6_address_mode=slaac --enable_dhcp"
@ -197,10 +169,6 @@ function pre_start_astara() {
typeset mgt_subnet_id=$(neutron $auth_args subnet-create mgt $ASTARA_MANAGEMENT_PREFIX $subnet_create_args | grep ' id ' | awk '{ print $4 }')
iniset $ASTARA_CONF DEFAULT management_subnet_id $mgt_subnet_id
# Remove the private network created by devstack
neutron $auth_args subnet-delete $PRIVATE_SUBNET_NAME
neutron $auth_args net-delete $PRIVATE_NETWORK_NAME
local astara_dev_image_src=""
local lb_element=""
@ -248,15 +216,6 @@ function pre_start_astara() {
fi
create_astara_nova_flavor
# Restart neutron so that `astara.floatingip_subnet` is properly set
if [[ "$USE_SCREEN" == "True" ]]; then
screen_stop_service q-svc
else
stop_process q-svc
fi
start_neutron_service_and_check
sleep 10
}
function start_astara() {
@ -268,13 +227,8 @@ function start_astara() {
}
function post_start_astara() {
echo "Creating demo user network and subnet"
local auth_args="$(_auth_args demo $OS_PASSWORD demo)"
neutron $auth_args net-create thenet
neutron $auth_args subnet-create thenet $FIXED_RANGE
# Open all traffic on the private CIDR
set_demo_tenant_sec_group_private_traffic
set_demo_tenant_sec_group_private_traffic
}
function stop_astara() {
@ -290,8 +244,6 @@ function set_neutron_user_permission() {
# public networks, we need to modify the policy and allow users with the service
# to do that too.
echo "Updating Nova policy file"
local old_value='"network:attach_external_network": "rule:admin_api"'
local new_value='"network:attach_external_network": "rule:admin_api or role:service"'
sed -i "s/$old_value/$new_value/g" "$NOVA_CONF_DIR/policy.json"
@ -328,7 +280,6 @@ if is_service_enabled astara; then
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
echo_summary "Installing Astara"
if is_service_enabled n-api; then
set_neutron_user_permission
fi

View File

@ -58,4 +58,6 @@ ASTARA_COORDINATION_URL=${ASTARA_COORDINATION_URL:-memcached://localhost:11211}
if [[ "$ASTARA_ENABLED_DRIVERS" =~ "router" ]]; then
ML2_L3_PLUGIN="astara_neutron.plugins.ml2_neutron_plugin.L3RouterPlugin"
Q_L3_ENABLED=True
Q_L3_ROUTER_PER_TENANT=True
fi

View File

@ -0,0 +1,17 @@
---
prelude: >
Astara has dropped a number of legacy convenience hooks available in
earlier releases. The hooks complicated automation and created
potential for mismatch of end state and the desired state.
fixes:
- Bug `1539345 <https://bugs.launchpad.net/astara/+bug/1539345>`_
auto added resources break interoperability
upgrade:
- Astara will no longer automatically add the external gateway to a router.
Previous usage was causing issues with automation tooling.
- Astara no longer requires the external network and subnet id to be known.
In production deployments this step was handled externally and the
internal hooks were often disabled.
critical:
- The devstack plugin no longer creates the external network as before and
instead follows the setup used for reference implementation.

View File

@ -26,6 +26,7 @@ os_tenant_name=$OS_TENANT_NAME
service_tenant_name=$SERVICE_TENANT_NAME
service_tenant_id=$SERVICE_TENANT_ID
appliance_api_port=$APPLIANCE_API_PORT
astara_auto_add_resources=False
# Defaults for the gate
health_check_timeout=10