kuryr-kubernetes/kuryr_kubernetes/controller/drivers/public_ip.py

181 lines
6.4 KiB
Python

# Copyright (c) 2017 RedHat, 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 abc
from kuryr_kubernetes import clients
from neutronclient.common import exceptions as n_exc
from oslo_log import log as logging
import six
LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class BasePubIpDriver(object):
"""Base class for public IP functionality."""
@abc.abstractmethod
def is_ip_available(self, ip_addr, port_id_to_be_associated):
"""check availability of ip address
:param ip_address:
:param port_id_to_be_associated
:returns res_id in case ip is available returns resources id else None
"""
raise NotImplementedError()
@abc.abstractmethod
def allocate_ip(self, pub_net_id, project_id, pub_subnet_id=None,
description=None, port_id_to_be_associated=None):
"""allocate ip address from public network id
:param pub_net_id: public network id
:param project_id:
:param pub_subnet_id: public subnet id (Optional)
:param description: string describing request (Optional)
:param port_id_to_be_associated: (optional)
:returns res_id , ip_addr
:res_id - resource id
:ip_addr - ip aaddress
"""
raise NotImplementedError()
@abc.abstractmethod
def free_ip(self, res_id):
"""free ip by resource ID
:param res_id: resource_id
:returns True/False
"""
raise NotImplementedError()
@abc.abstractmethod
def associate(self, res_id, vip_port_id):
"""Associate VIP port id with resource_id
:param res_id: id represents pub ip resource
:param vip_port_id: VIP port id
"""
raise NotImplementedError()
@abc.abstractmethod
def disassociate(self, res_id):
"""Clear association between res_id to any vip port
:param res_id: id represents pub ip resource
"""
class FipPubIpDriver(BasePubIpDriver):
"""Floating IP implementation for public IP capability ."""
def is_ip_available(self, ip_addr, port_id_to_be_associated=None):
if ip_addr:
neutron = clients.get_neutron_client()
floating_ips_list = neutron.list_floatingips(
floating_ip_address=ip_addr)
for entry in floating_ips_list['floatingips']:
if not entry:
continue
if (entry['floating_ip_address'] == ip_addr):
if not entry['port_id'] or (
port_id_to_be_associated is not None
and entry['port_id'] == port_id_to_be_associated):
return entry['id']
# floating IP not available
LOG.error("Floating IP=%s not available", ip_addr)
else:
LOG.error("Invalid parameter ip_addr=%s", ip_addr)
return None
def allocate_ip(self, pub_net_id, project_id, pub_subnet_id=None,
description=None, port_id_to_be_associated=None):
neutron = clients.get_neutron_client()
if port_id_to_be_associated is not None:
floating_ips_list = neutron.list_floatingips(
port_id=port_id_to_be_associated)
for entry in floating_ips_list['floatingips']:
if not entry:
continue
if (entry['floating_ip_address']):
LOG.debug('FIP %s already allocated to port %s',
entry['floating_ip_address'],
port_id_to_be_associated)
return entry['id'], entry['floating_ip_address']
request = {'floatingip': {
'tenant_id': project_id,
'project_id': project_id,
'floating_network_id': pub_net_id}}
if pub_subnet_id is not None:
request['floatingip']['subnet_id'] = pub_subnet_id
if description is not None:
request['floatingip']['description'] = description
try:
response = neutron.create_floatingip(request)
except n_exc.NeutronClientException:
LOG.exception("Failed to create floating IP - netid=%s ",
pub_net_id)
raise
return response['floatingip']['id'], response[
'floatingip']['floating_ip_address']
def free_ip(self, res_id):
neutron = clients.get_neutron_client()
try:
neutron.delete_floatingip(res_id)
except n_exc.NeutronClientException:
LOG.error("Failed to delete floating_ip_id =%s !",
res_id)
return False
return True
def _update(self, res_id, vip_port_id):
response = None
neutron = clients.get_neutron_client()
try:
response = neutron.update_floatingip(
res_id, {'floatingip': {'port_id': vip_port_id, }})
except n_exc.Conflict:
LOG.warning("Conflict when assigning floating IP with id %s. "
"Checking if it's already assigned correctly.", res_id)
fip = neutron.show_floatingip(res_id).get('floatingip')
if fip is not None and fip.get('port_id') == vip_port_id:
LOG.debug('FIP %s already assigned to %s', res_id,
vip_port_id)
else:
LOG.exception('Failed to assign FIP %s to VIP port %s. It is '
'probably already bound', res_id, vip_port_id)
raise
except n_exc.NeutronClientException:
LOG.error("Failed to update_floatingip ,floating_ip_id=%s,"
"response=%s!", res_id, response)
raise
def associate(self, res_id, vip_port_id):
self._update(res_id, vip_port_id)
def disassociate(self, res_id):
self._update(res_id, None)