598 lines
24 KiB
Python
598 lines
24 KiB
Python
# Copyright 2018 Canonical Ltd
|
|
#
|
|
# 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.
|
|
|
|
# NOTE(fnordahl) imported dependencies are included in the reactive charm
|
|
# ``wheelhouse.txt`` and are isolated from any system installed payload managed
|
|
# by the charm.
|
|
#
|
|
# An alternative could be to execute the openstack CLI to manage the resources,
|
|
# but at the time of this writing we can not due to it producing invalid JSON
|
|
# and YAML for the ``fixed_ips`` field when providing details for a Neutron
|
|
# port.
|
|
|
|
import neutronclient
|
|
import subprocess
|
|
|
|
from keystoneauth1 import identity as keystone_identity
|
|
from keystoneauth1 import session as keystone_session
|
|
from keystoneauth1 import exceptions as keystone_exceptions
|
|
from neutronclient.v2_0 import client as neutron_client
|
|
from novaclient import client as nova_client
|
|
|
|
import charm.openstack.octavia as octavia # for constants
|
|
|
|
import charmhelpers.core as ch_core
|
|
import charmhelpers.contrib.network.ip as ch_net_ip
|
|
|
|
|
|
NEUTRON_TEMP_EXCS = (keystone_exceptions.catalog.EndpointNotFound,
|
|
keystone_exceptions.connection.ConnectFailure,
|
|
keystone_exceptions.discovery.DiscoveryFailure,
|
|
keystone_exceptions.http.ServiceUnavailable,
|
|
neutronclient.common.exceptions.ServiceUnavailable)
|
|
|
|
|
|
class APIUnavailable(Exception):
|
|
"""Exception raised when a temporary availability issue occurs."""
|
|
|
|
def __init__(self, service_type, resource_type, upstream_exception):
|
|
"""Initialize APIUnavailable exception.
|
|
|
|
:param service_type: Name of service we had issues with (e.g. `nova`).
|
|
:type service_type: str
|
|
:param resource_type: Name of resource we had issues with
|
|
(e.g. `flavors`)
|
|
:type resource_type: str
|
|
:param upstream_exception: Reference to the exception caught
|
|
:type upstream_exception: BaseException derived object
|
|
"""
|
|
self.service_type = service_type
|
|
self.resource_type = resource_type
|
|
self.upstream_exception = upstream_exception
|
|
|
|
|
|
class DuplicateResource(Exception):
|
|
"""Exception raised when resource query result in multiple entries."""
|
|
|
|
def __init__(self, service_type, resource_type, data=None):
|
|
"""Initialize DuplicateResource exception.
|
|
|
|
:param service_type: Name of service we had issues with (e.g. `nova`).
|
|
:type service_type: str
|
|
:param resource_type: Name of resource we had issues with
|
|
(e.g. `flavors`)
|
|
:type resource_type: str
|
|
:param data: Data from search result
|
|
:type data: (Optional)any
|
|
"""
|
|
self.service_type = service_type
|
|
self.resource_type = resource_type
|
|
self.data = data
|
|
|
|
|
|
def session_from_identity_service(identity_service):
|
|
"""Get Keystone Session from `identity-service` relation.
|
|
|
|
:param identity_service: reactive Endpoint
|
|
:type identity_service: RelationBase
|
|
:returns: Keystone session
|
|
:rtype: keystone_session.Session
|
|
"""
|
|
auth = keystone_identity.Password(
|
|
auth_url='{}://{}:{}/'
|
|
.format(identity_service.auth_protocol(),
|
|
identity_service.auth_host(),
|
|
identity_service.auth_port()),
|
|
user_domain_name=identity_service.service_domain(),
|
|
username=identity_service.service_username(),
|
|
password=identity_service.service_password(),
|
|
project_domain_name=identity_service.service_domain(),
|
|
project_name=identity_service.service_tenant(),
|
|
)
|
|
return keystone_session.Session(auth=auth)
|
|
|
|
|
|
def get_nova_flavor(identity_service):
|
|
"""Get or create private Nova flavor for use with Octavia.
|
|
|
|
A side effect of calling this function is that Nova flavors are
|
|
created if they do not already exist.
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:returns: Nova Flavor Resource object
|
|
:rtype: novaclient.v2.flavors.Flavor
|
|
"""
|
|
try:
|
|
session = session_from_identity_service(identity_service)
|
|
nova = nova_client.Client('2', session=session)
|
|
flavors = nova.flavors.list(is_public=False)
|
|
for flavor in flavors:
|
|
if flavor.name == 'charm-octavia':
|
|
return flavor
|
|
|
|
# create flavor
|
|
return nova.flavors.create('charm-octavia', 1024, 1, 8,
|
|
is_public=False)
|
|
except (keystone_exceptions.catalog.EndpointNotFound,
|
|
keystone_exceptions.connection.ConnectFailure,
|
|
nova_client.exceptions.ConnectionRefused,
|
|
nova_client.exceptions.ClientException) as e:
|
|
raise APIUnavailable('nova', 'flavors', e)
|
|
|
|
|
|
def get_hm_port(identity_service, local_unit_name, local_unit_address):
|
|
"""Get or create a per unit Neutron port for Octavia Health Manager.
|
|
|
|
A side effect of calling this function is that a port is created if one
|
|
does not already exist.
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:param local_unit_name: Name of juju unit, used to build tag name for port
|
|
:type local_unit_name: str
|
|
:param local_unit_address: DNS resolvable IP address of unit, used to
|
|
build Neutron port ``binding:host_id``
|
|
:type local_unit_address: str
|
|
:returns: Port details extracted from result of call to
|
|
neutron_client.list_ports or neutron_client.create_port
|
|
:rtype: dict
|
|
:raises: api_crud.APIUnavailable, api_crud.DuplicateResource
|
|
"""
|
|
session = session_from_identity_service(identity_service)
|
|
try:
|
|
nc = neutron_client.Client(session=session)
|
|
resp = nc.list_networks(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'networks', e)
|
|
|
|
network = None
|
|
n_resp = len(resp.get('networks', []))
|
|
if n_resp == 1:
|
|
network = resp['networks'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'networks', data=resp)
|
|
else:
|
|
ch_core.hookenv.log('No network tagged with `charm-octavia` exists, '
|
|
'deferring port setup awaiting network and port '
|
|
'(re-)creation.', level=ch_core.hookenv.WARNING)
|
|
return
|
|
health_secgrp = None
|
|
|
|
try:
|
|
resp = nc.list_security_groups(tags='charm-octavia-health')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
|
|
n_resp = len(resp.get('security_groups', []))
|
|
if n_resp == 1:
|
|
health_secgrp = resp['security_groups'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'security_groups', data=resp)
|
|
else:
|
|
ch_core.hookenv.log('No security group tagged with '
|
|
'`charm-octavia-health` exists, deferring '
|
|
'port setup awaiting network and port '
|
|
'(re-)creation...',
|
|
level=ch_core.hookenv.WARNING)
|
|
return
|
|
|
|
try:
|
|
resp = nc.list_ports(tags='charm-octavia-{}'
|
|
.format(local_unit_name))
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'ports', e)
|
|
|
|
n_resp = len(resp.get('ports', []))
|
|
if n_resp == 1:
|
|
hm_port = resp['ports'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'ports', data=resp)
|
|
else:
|
|
# create new port
|
|
try:
|
|
resp = nc.create_port(
|
|
{
|
|
'port': {
|
|
# avoid race with OVS agent attempting to bind port
|
|
# before it is created in the local units OVSDB
|
|
'admin_state_up': False,
|
|
'binding:host_id': ch_net_ip.get_hostname(
|
|
local_unit_address, fqdn=False),
|
|
'device_owner': 'Octavia:health-mgr',
|
|
'security_groups': [
|
|
health_secgrp['id'],
|
|
],
|
|
'name': 'octavia-health-manager-{}-listen-port'
|
|
.format(local_unit_name),
|
|
'network_id': network['id'],
|
|
},
|
|
})
|
|
hm_port = resp['port']
|
|
ch_core.hookenv.log('Created port {}'.format(hm_port['id']),
|
|
ch_core.hookenv.INFO)
|
|
# unit specific tag is used by each unit to load their state
|
|
nc.add_tag('ports', hm_port['id'],
|
|
'charm-octavia-{}'
|
|
.format(local_unit_name))
|
|
# charm-wide tag is used by leader to load cluster state and build
|
|
# ``controller_ip_port_list`` configuration property
|
|
nc.add_tag('ports', hm_port['id'], 'charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'ports', e)
|
|
return hm_port
|
|
|
|
|
|
def toggle_hm_port(identity_service, local_unit_name, enabled=True):
|
|
"""Toggle administrative state of Neutron port for local unit.
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:param local_unit_name: Name of juju unit, used to build tag name for port
|
|
:type local_unit_name: str
|
|
:param enabled: Desired state
|
|
:type enabled: bool
|
|
:raises: api_crud.APIUnavailable
|
|
"""
|
|
session = session_from_identity_service(identity_service)
|
|
try:
|
|
nc = neutron_client.Client(session=session)
|
|
resp = nc.list_ports(tags='charm-octavia-{}'
|
|
.format(local_unit_name))
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'ports', e)
|
|
for port in (resp['ports']):
|
|
nc.update_port(port['id'], {'port': {'admin_state_up': enabled}})
|
|
|
|
|
|
def setup_hm_port(identity_service, octavia_charm):
|
|
"""Create a per unit Neutron and OVS port for Octavia Health Manager.
|
|
|
|
This is used to plug the unit into the overlay network for direct
|
|
communication with the octavia managed load balancer instances running
|
|
within the deployed cloud.
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:param ocataiva_charm: charm instance
|
|
:type octavia_charm: OctaviaCharm class instance
|
|
:retruns: True on change to local unit, False otherwise
|
|
:rtype: bool
|
|
:raises: api_crud.APIUnavailable, api_crud.DuplicateResource
|
|
"""
|
|
unit_changed = False
|
|
hm_port = get_hm_port(
|
|
identity_service,
|
|
octavia_charm.local_unit_name,
|
|
octavia_charm.local_address)
|
|
if not hm_port:
|
|
ch_core.hookenv.log('No network tagged with `charm-octavia` '
|
|
'exists, deferring port setup awaiting '
|
|
'network and port (re-)creation...',
|
|
level=ch_core.hookenv.WARNING)
|
|
return
|
|
HM_PORT_MAC = hm_port['mac_address']
|
|
HM_PORT_ID = hm_port['id']
|
|
try:
|
|
subprocess.check_output(
|
|
['ip', 'link', 'show', octavia.OCTAVIA_MGMT_INTF],
|
|
stderr=subprocess.STDOUT, universal_newlines=True)
|
|
except subprocess.CalledProcessError as e:
|
|
if 'does not exist' in e.output:
|
|
subprocess.check_call(
|
|
['ovs-vsctl', '--', 'add-port',
|
|
octavia.OCTAVIA_INT_BRIDGE, octavia.OCTAVIA_MGMT_INTF,
|
|
'--', 'set', 'Interface', octavia.OCTAVIA_MGMT_INTF,
|
|
'type=internal',
|
|
'--', 'set', 'Interface', octavia.OCTAVIA_MGMT_INTF,
|
|
'external-ids:iface-status=active',
|
|
'--', 'set', 'Interface', octavia.OCTAVIA_MGMT_INTF,
|
|
'external-ids:attached-mac={}'.format(HM_PORT_MAC),
|
|
'--', 'set', 'Interface', octavia.OCTAVIA_MGMT_INTF,
|
|
'external-ids:iface-id={}'.format(HM_PORT_ID),
|
|
'--', 'set', 'Interface', octavia.OCTAVIA_MGMT_INTF,
|
|
'external-ids:skip_cleanup=true',
|
|
])
|
|
ch_core.hookenv.log('add OVS port', level=ch_core.hookenv.INFO)
|
|
# post boot reconfiguration of systemd-networkd does not appear to
|
|
# set the MAC addresss on the interface, do it ourself.
|
|
subprocess.check_call(
|
|
['ip', 'link', 'set', octavia.OCTAVIA_MGMT_INTF,
|
|
'up', 'address', HM_PORT_MAC])
|
|
# Signal that change has been made to local unit
|
|
unit_changed = True
|
|
else:
|
|
# unknown error, raise
|
|
raise e
|
|
if not hm_port['admin_state_up'] or hm_port['status'] == 'DOWN':
|
|
# NOTE(fnordahl) there appears to be a handful of race conditions
|
|
# hitting us sometimes making the newly created ports unusable.
|
|
# as a workaround we toggle the port belonging to us.
|
|
# a disable/enable round trip makes Neutron reset the port
|
|
# configuration which resolves these situations.
|
|
ch_core.hookenv.log('toggling port {} (admin_state_up: {} '
|
|
'status: {})'
|
|
.format(hm_port['id'],
|
|
hm_port['admin_state_up'],
|
|
hm_port['status']),
|
|
level=ch_core.hookenv.INFO)
|
|
|
|
toggle_hm_port(identity_service,
|
|
octavia_charm.local_unit_name,
|
|
enabled=False)
|
|
toggle_hm_port(identity_service,
|
|
octavia_charm.local_unit_name,
|
|
enabled=True)
|
|
return unit_changed
|
|
|
|
|
|
def get_port_ips(identity_service):
|
|
"""Extract IP information from Neutron ports tagged with ``charm-octavia``
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:returns: List of IP addresses extracted from port details in search result
|
|
:rtype: list of str
|
|
:raises: api_crud.APIUnavailable
|
|
"""
|
|
session = session_from_identity_service(identity_service)
|
|
try:
|
|
nc = neutron_client.Client(session=session)
|
|
resp = nc.list_ports(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'ports', e)
|
|
|
|
neutron_ip_list = []
|
|
for port in resp['ports']:
|
|
for ip_info in port['fixed_ips']:
|
|
neutron_ip_list.append(ip_info['ip_address'])
|
|
|
|
return neutron_ip_list
|
|
|
|
|
|
def get_mgmt_network(identity_service, create=True):
|
|
"""Get or create Neutron network resources for Octavia.
|
|
|
|
A side effect of calling this function is that network resources are
|
|
created if they do not already exist, unless ``create`` is set to False.
|
|
|
|
:param identity_service: reactive Endpoint of type ``identity-service``
|
|
:type identity_service: RelationBase class
|
|
:param create: (Optional and default) Create resources that do not exist
|
|
:type: create: bool
|
|
:returns: List of IP addresses extracted from port details in search result
|
|
:rtype: list of str
|
|
:raises: api_crud.APIUnavailable, api_crud.DuplicateResource
|
|
"""
|
|
session = session_from_identity_service(identity_service)
|
|
try:
|
|
nc = neutron_client.Client(session=session)
|
|
resp = nc.list_networks(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'networks', e)
|
|
|
|
n_resp = len(resp.get('networks', []))
|
|
if n_resp == 1:
|
|
network = resp['networks'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'networks', data=resp)
|
|
elif not create:
|
|
ch_core.hookenv.log('No network tagged with `charm-octavia` exists, '
|
|
'and we are configured to not create resources.'
|
|
'Awaiting end user resource creation.',
|
|
level=ch_core.hookenv.WARNING)
|
|
return
|
|
else:
|
|
try:
|
|
resp = nc.create_network({
|
|
'network': {'name': octavia.OCTAVIA_MGMT_NET}})
|
|
network = resp['network']
|
|
nc.add_tag('networks', network['id'], 'charm-octavia')
|
|
ch_core.hookenv.log('Created network {}'.format(network['id']),
|
|
level=ch_core.hookenv.INFO)
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'networks', e)
|
|
|
|
try:
|
|
resp = nc.list_subnets(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'subnets', e)
|
|
|
|
n_resp = len(resp.get('subnets', []))
|
|
subnets = None
|
|
if n_resp < 1 and create:
|
|
# make rfc4193 Unique Local IPv6 Unicast Addresses from network UUID
|
|
rfc4193_addr = 'fc00'
|
|
for n in [0, 4, 8]:
|
|
rfc4193_addr += ':' + network['id'].split('-')[4][n:n + 4]
|
|
rfc4193_addr += '::/64'
|
|
try:
|
|
resp = nc.create_subnet(
|
|
{
|
|
'subnets': [
|
|
{
|
|
'name': octavia.OCTAVIA_MGMT_SUBNET + 'v6',
|
|
'ip_version': 6,
|
|
'ipv6_address_mode': 'slaac',
|
|
'ipv6_ra_mode': 'slaac',
|
|
'cidr': rfc4193_addr,
|
|
'network_id': network['id'],
|
|
},
|
|
],
|
|
})
|
|
subnets = resp['subnets']
|
|
for subnet in resp['subnets']:
|
|
nc.add_tag('subnets', subnet['id'], 'charm-octavia')
|
|
ch_core.hookenv.log('Created subnet {} with cidr {}'
|
|
.format(subnet['id'], subnet['cidr']),
|
|
level=ch_core.hookenv.INFO)
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'subnets', e)
|
|
|
|
try:
|
|
resp = nc.list_routers(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'routers', e)
|
|
|
|
n_resp = len(resp.get('routers', []))
|
|
router = None
|
|
if n_resp < 1 and create:
|
|
try:
|
|
resp = nc.create_router(
|
|
{
|
|
'router': {
|
|
'name': octavia.OCTAVIA_MGMT_NAME_PREFIX,
|
|
|
|
}
|
|
})
|
|
router = resp['router']
|
|
nc.add_tag('routers', router['id'], 'charm-octavia')
|
|
ch_core.hookenv.log('Created router {}'.format(router['id']),
|
|
level=ch_core.hookenv.INFO)
|
|
for subnet in subnets:
|
|
nc.add_interface_router(router['id'],
|
|
{'subnet_id': subnet['id']})
|
|
ch_core.hookenv.log('Added interface from router {} '
|
|
'to subnet {}'
|
|
.format(router['id'], subnet['id']),
|
|
level=ch_core.hookenv.INFO)
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'routers', e)
|
|
|
|
try:
|
|
resp = nc.list_security_groups(tags='charm-octavia')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
|
|
n_resp = len(resp.get('security_groups', []))
|
|
if n_resp == 1:
|
|
secgrp = resp['security_groups'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'security_groups', data=resp)
|
|
elif not create:
|
|
ch_core.hookenv.log('No security group tagged with `charm-octavia` '
|
|
'exists, and we are configured to not create '
|
|
'resources. Awaiting end user resource '
|
|
'creation.',
|
|
level=ch_core.hookenv.WARNING)
|
|
return
|
|
else:
|
|
try:
|
|
resp = nc.create_security_group(
|
|
{
|
|
'security_group': {
|
|
'name': octavia.OCTAVIA_MGMT_SECGRP,
|
|
},
|
|
})
|
|
secgrp = resp['security_group']
|
|
nc.add_tag('security_groups', secgrp['id'], 'charm-octavia')
|
|
ch_core.hookenv.log('Created security group "{}"'
|
|
.format(secgrp['id']),
|
|
level=ch_core.hookenv.INFO)
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
|
|
if create:
|
|
security_group_rules = [
|
|
{
|
|
'direction': 'ingress',
|
|
'protocol': 'icmpv6',
|
|
'ethertype': 'IPv6',
|
|
'security_group_id': secgrp['id'],
|
|
},
|
|
{
|
|
'direction': 'ingress',
|
|
'protocol': 'tcp',
|
|
'ethertype': 'IPv6',
|
|
'port_range_min': '22',
|
|
'port_range_max': '22',
|
|
'security_group_id': secgrp['id'],
|
|
},
|
|
{
|
|
'direction': 'ingress',
|
|
'protocol': 'tcp',
|
|
'ethertype': 'IPv6',
|
|
'port_range_min': '9443',
|
|
'port_range_max': '9443',
|
|
'security_group_id': secgrp['id'],
|
|
},
|
|
]
|
|
for rule in security_group_rules:
|
|
try:
|
|
nc.create_security_group_rule({'security_group_rule': rule})
|
|
except neutronclient.common.exceptions.Conflict:
|
|
pass
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_group_rules', e)
|
|
|
|
try:
|
|
resp = nc.list_security_groups(tags='charm-octavia-health')
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
|
|
n_resp = len(resp.get('security_groups', []))
|
|
if n_resp == 1:
|
|
health_secgrp = resp['security_groups'][0]
|
|
elif n_resp > 1:
|
|
raise DuplicateResource('neutron', 'security_groups', data=resp)
|
|
elif not create:
|
|
ch_core.hookenv.log('No security group tagged with '
|
|
'`charm-octavia-health` exists, and we are '
|
|
'configured to not create resources. Awaiting '
|
|
'end user resource creation.',
|
|
level=ch_core.hookenv.WARNING)
|
|
return
|
|
else:
|
|
try:
|
|
resp = nc.create_security_group(
|
|
{
|
|
'security_group': {
|
|
'name': octavia.OCTAVIA_HEALTH_SECGRP,
|
|
},
|
|
})
|
|
health_secgrp = resp['security_group']
|
|
nc.add_tag('security_groups', health_secgrp['id'],
|
|
'charm-octavia-health')
|
|
ch_core.hookenv.log('Created security group "{}"'
|
|
.format(health_secgrp['id']),
|
|
level=ch_core.hookenv.INFO)
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
if create:
|
|
health_security_group_rules = [
|
|
{
|
|
'direction': 'ingress',
|
|
'protocol': 'icmpv6',
|
|
'ethertype': 'IPv6',
|
|
'security_group_id': health_secgrp['id'],
|
|
},
|
|
{
|
|
'direction': 'ingress',
|
|
'protocol': 'udp',
|
|
'ethertype': 'IPv6',
|
|
'port_range_min': octavia.OCTAVIA_HEALTH_LISTEN_PORT,
|
|
'port_range_max': octavia.OCTAVIA_HEALTH_LISTEN_PORT,
|
|
'security_group_id': health_secgrp['id'],
|
|
},
|
|
]
|
|
for rule in health_security_group_rules:
|
|
try:
|
|
nc.create_security_group_rule({'security_group_rule': rule})
|
|
except neutronclient.common.exceptions.Conflict:
|
|
pass
|
|
except NEUTRON_TEMP_EXCS as e:
|
|
raise APIUnavailable('neutron', 'security_groups', e)
|
|
resp = nc.list_security_group_rules(security_group_id=health_secgrp['id'])
|
|
return (network, secgrp)
|