Skip to content

Instantly share code, notes, and snippets.

@rm-you

rm-you/flips.py Secret

Last active February 17, 2017 21:32
Show Gist options
  • Save rm-you/1a37185a6688da2a0f4464559d7ee67b to your computer and use it in GitHub Desktop.
Save rm-you/1a37185a6688da2a0f4464559d7ee67b to your computer and use it in GitHub Desktop.
# Copyright 2014 Rackspace
# Copyright 2017 GoDaddy
#
# 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 six
from neutronclient.common import exceptions as neutron_client_exceptions
from oslo_config import cfg
from oslo_log import log as logging
from octavia.common import constants
from octavia.common import data_models
from octavia.controller.worker.tasks import network_tasks
from octavia.i18n import _LE, _LI, _LW
from octavia.network import base
from octavia.network import data_models as network_models
from octavia.network.drivers.neutron import allowed_address_pairs
from octavia.network.drivers.neutron import utils
LOG = logging.getLogger(__name__)
VIP_SECURITY_GRP_PREFIX = 'lb-'
CONF = cfg.CONF
class FloatingipNotFound(base.NetworkException):
pass
base.FloatingipNotFound = FloatingipNotFound
class FloatingIP(data_models.BaseDataModel):
def __init__(self, id=None, description=None, project_id=None,
status=None, router_id=None, port_id=None,
floating_network_id=None, floating_ip_address=None,
fixed_ip_address=None, fixed_port_id=None):
self.id = id
self.description = description
self.project_id = project_id
self.status = status
self.router_id = router_id
self.port_id = port_id
self.floating_network_id = floating_network_id
self.floating_ip_address = floating_ip_address
self.fixed_ip_address = fixed_ip_address
self.fixed_port_id = fixed_port_id
network_models.FloatingIP = FloatingIP
def convert_floatingip_dict_to_model(floating_ip_dict):
floating_ip = floating_ip_dict.get('floatingip', floating_ip_dict)
return FloatingIP(
id=floating_ip.get('id'),
description=floating_ip.get('description'),
project_id=floating_ip.get('project_id', floating_ip.get('tenant_id')),
status=floating_ip.get('status'),
router_id=floating_ip.get('router_id'),
port_id=floating_ip.get('port_id'),
floating_network_id=floating_ip.get('floating_network_id'),
floating_ip_address=floating_ip.get('floating_ip_address'),
fixed_ip_address=floating_ip.get('fixed_ip_address'),
fixed_port_id=floating_ip.get('fixed_port_id')
)
utils.convert_floatingip_dict_to_model = convert_floatingip_dict_to_model
class CalculateAmphoraDelta(network_tasks.BaseNetworkTask):
default_provides = constants.DELTA
def execute(self, loadbalancer, amphora):
LOG.debug("Calculating network delta for amphora id: %s", amphora.id)
# Figure out what networks we want
# seed with lb network(s)
vrrp_port = self.network_driver.get_port(amphora.vrrp_port_id)
desired_network_ids = {vrrp_port.network_id}.union(
CONF.controller_worker.amp_boot_network_list)
for pool in loadbalancer.pools:
member_networks = [
self.network_driver.get_subnet(member.subnet_id).network_id
for member in pool.members
if member.subnet_id
]
desired_network_ids.update(member_networks)
nics = self.network_driver.get_plugged_networks(amphora.compute_id)
# assume we don't have two nics in the same network
actual_network_nics = dict((nic.network_id, nic) for nic in nics)
del_ids = set(actual_network_nics) - desired_network_ids
delete_nics = list(
actual_network_nics[net_id] for net_id in del_ids)
add_ids = desired_network_ids - set(actual_network_nics)
add_nics = list(network_models.Interface(
network_id=net_id) for net_id in add_ids)
delta = network_models.Delta(
amphora_id=amphora.id, compute_id=amphora.compute_id,
add_nics=add_nics, delete_nics=delete_nics)
return delta
network_tasks.CalculateAmphoraDelta = CalculateAmphoraDelta
class FloatingipDriver(allowed_address_pairs.AllowedAddressPairsDriver):
def __init__(self):
super(FloatingipDriver, self).__init__()
self.project_id = self.neutron_client.get_auth_info().get(
'auth_tenant_id')
def _associate_floatingip(self, flip_id, port_id):
body = {"floatingip": {"port_id": port_id}}
self.neutron_client.update_floatingip(flip_id, body)
def _disassociate_floatingip(self, flip_id):
body = {"floatingip": {"port_id": None}}
self.neutron_client.update_floatingip(flip_id, body)
def get_floatingip(self, flip_id):
return self._get_resource('floatingip', flip_id)
def _flip_to_vip(self, flip, load_balancer):
return data_models.Vip(ip_address=flip.floating_ip_address,
network_id=flip.floating_network_id,
port_id=flip.id,
load_balancer=load_balancer,
load_balancer_id=load_balancer.id)
def _get_mgmt_subnet(self, compute_id, mgmt_ip):
interfaces = self.get_plugged_networks(compute_id)
for interface in interfaces:
for ip in interface.fixed_ips:
if ip.ip_address == mgmt_ip:
subnet = self.get_subnet(ip.subnet_id)
return subnet
def _update_vip_security_group(self, load_balancer, vip):
sec_grp = self._get_lb_security_group(load_balancer.id)
if not sec_grp:
sec_grp_name = VIP_SECURITY_GRP_PREFIX + load_balancer.id
sec_grp = self._create_security_group(sec_grp_name)
self._update_security_group_rules(load_balancer, sec_grp.get('id'))
def get_network_configs(self, loadbalancer):
vip_port = self.get_floatingip(loadbalancer.vip.port_id)
amp_configs = {}
for amp in loadbalancer.amphorae:
if amp.status != constants.DELETED:
LOG.debug("Retrieving network details for amphora %s", amp.id)
vrrp_port = self.get_port(amp.vrrp_port_id)
vrrp_subnet = self.get_subnet(
vrrp_port.get_subnet_id(amp.vrrp_ip))
vrrp_port.network = self.get_network(vrrp_port.network_id)
amp_configs[amp.id] = network_models.AmphoraNetworkConfig(
amphora=amp,
vip_port=vip_port,
vip_subnet=vrrp_subnet,
vrrp_subnet=vrrp_subnet,
vrrp_port=vrrp_port,
ha_subnet=vrrp_subnet,
ha_port=vip_port
)
return amp_configs
def allocate_vip(self, load_balancer):
if load_balancer.vip.port_id:
LOG.info(_LI('Floating ip %s already exists. Nothing to be done.'),
load_balancer.vip.port_id)
flip = self.get_floatingip(load_balancer.vip.port_id)
return self._flip_to_vip(flip, load_balancer)
# It can be assumed that network_id exists
flip = {'floatingip': {
# 'description': 'octavia-lb-' + load_balancer.id,
'floating_network_id': load_balancer.vip.network_id}}
try:
new_flip = self.neutron_client.create_floatingip(flip)
except Exception:
message = _('Error creating floating ip on network '
'{network_id}.').format(
network_id=load_balancer.vip.network_id)
LOG.exception(message)
raise base.AllocateVIPException(message)
new_flip = convert_floatingip_dict_to_model(new_flip)
return self._flip_to_vip(new_flip, load_balancer)
def deallocate_vip(self, vip):
"""Delete the vrrp_port (instance port) in case nova didn't
This can happen if a failover has occurred.
"""
if vip.port_id:
try:
flip = self.get_floatingip(vip.port_id)
if flip.project_id == self.project_id:
try:
self.neutron_client.delete_floatingip(vip.port_id)
except Exception:
message = _(
'Error deleting VIP floating ip {flip_id} from '
'neutron').format(flip_id=vip.port_id)
LOG.exception(message)
raise base.DeallocateVIPException(message)
else:
LOG.info(_LI(
"Floating ip %s will not be deleted by Octavia as it "
"was not created by Octavia. Disassociating it "
"instead."), vip.port_id)
try:
self._disassociate_floatingip(flip.id)
except Exception:
message = _(
"Can't disassociate VIP floating ip {0}.").format(
vip.port_id)
raise base.DeallocateVIPException(message)
except FloatingipNotFound:
LOG.warning(_LW("Floating ip {0} wasn't found in neutron "
"while deallocating the VIP, skipping..."
).format(vip.port_id))
# Carried forward from AAP deallocate_vip, since we DO have these
try:
for amphora in six.moves.filter(self._filter_amphora,
vip.load_balancer.amphorae):
self.neutron_client.delete_port(amphora.vrrp_port_id)
except (neutron_client_exceptions.NotFound,
neutron_client_exceptions.PortNotFoundClient):
LOG.debug('VIP instance port {0} already deleted. '
'Skipping.'.format(amphora.vrrp_port_id))
def plug_vip(self, load_balancer, vip):
if self.sec_grp_enabled:
self._update_vip_security_group(load_balancer, vip)
plugged_amphorae = []
for amphora in six.moves.filter(
lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
load_balancer.amphorae):
mgmt_subnet = self._get_mgmt_subnet(
amphora.compute_id, amphora.lb_network_ip)
interface = self._get_plugged_interface(
amphora.compute_id, mgmt_subnet.network_id,
amphora.lb_network_ip)
if not interface:
interface = self._plug_amphora_vip(amphora, mgmt_subnet)
self._add_vip_address_pair(interface.port_id, vip.ip_address)
if self.sec_grp_enabled:
self._add_vip_security_group_to_port(load_balancer.id,
interface.port_id)
if amphora.role in (constants.ROLE_STANDALONE,
constants.ROLE_MASTER):
self._associate_floatingip(vip.port_id, interface.port_id)
vrrp_ip = None
for fixed_ip in interface.fixed_ips:
is_correct_subnet = fixed_ip.subnet_id == mgmt_subnet.id
is_management_ip = fixed_ip.ip_address == amphora.lb_network_ip
if is_correct_subnet and not is_management_ip:
vrrp_ip = fixed_ip.ip_address
break
plugged_amphorae.append(data_models.Amphora(
id=amphora.id,
compute_id=amphora.compute_id,
vrrp_ip=vrrp_ip,
ha_ip=vip.ip_address,
vrrp_port_id=interface.port_id,
ha_port_id=vip.port_id))
return plugged_amphorae
def unplug_vip(self, load_balancer, vip):
try:
flip = self.get_floatingip(vip.port_id)
except FloatingipNotFound:
msg = _LE("Can't unplug vip because floating ip {0} was not "
"found").format(vip.port_id)
LOG.exception(msg)
raise base.PluggedVIPNotFound(msg)
for amphora in six.moves.filter(
lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
load_balancer.amphorae):
mgmt_subnet = self._get_mgmt_subnet(
amphora.compute_id, amphora.lb_network_ip)
interface = self._get_plugged_interface(
amphora.compute_id, mgmt_subnet.network_id,
amphora.lb_network_ip)
if not interface:
# Thought about raising PluggedVIPNotFound exception but
# then that wouldn't evaluate all amphorae, so just continue
LOG.debug(_LI('Cannot get amphora %s interface, skipped'),
amphora.compute_id)
continue
try:
self.unplug_network(amphora.compute_id, mgmt_subnet.network_id)
except Exception:
pass
if flip.fixed_port_id == amphora.vrrp_port_id:
try:
self._disassociate_floatingip(flip.id)
except Exception:
message = _('Error unplugging VIP. Could not disassociate '
'floating ip from port {port_id}.'
).format(port_id=vip.port_id)
LOG.exception(message)
raise base.UnplugVIPException(message)
# Delete the VRRP port if we created it
try:
port = self.get_port(amphora.vrrp_port_id)
if port.name.startswith('octavia-lb-vrrp-'):
self.neutron_client.delete_port(amphora.vrrp_port_id)
except base.PortNotFound:
pass
except Exception as e:
LOG.error(_LE('Failed to delete port. Resources may still '
'be in use for port: %(port)s due to '
'error: %s(except)s'),
{'port': amphora.vrrp_port_id, 'except': e})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment