Delete SSH amphora driver

The old SSH amphora driver is not being used by anyone
anymore, nor is it being maintained. This patch removes it from
the Octavia code tree.

Closes-Bug: 1534218
Change-Id: I006f1c794e1ab0483886d06495ca6649f0afe479
This commit is contained in:
Stephen Balukoff 2016-02-17 02:28:14 -08:00
parent 27ca78a5de
commit 2a0a0944bf
6 changed files with 36 additions and 651 deletions

View File

@ -140,8 +140,7 @@
# client_ca = /etc/octavia/certs/ca_01.pem
# Amphora driver options are amphora_noop_driver,
# amphora_haproxy_rest_driver,
# amphora_haproxy_ssh_driver
# amphora_haproxy_rest_driver
#
# amphora_driver = amphora_noop_driver
#

View File

@ -1,310 +0,0 @@
# Copyright (c) 2015 Rackspace
#
# 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 os
import socket
import tempfile
import time
from oslo_log import log as logging
import paramiko
import six
from stevedore import driver as stevedore_driver
from octavia.amphorae.driver_exceptions import exceptions as exc
from octavia.amphorae.drivers import driver_base as driver_base
from octavia.amphorae.drivers.haproxy.jinja import jinja_cfg
from octavia.common.config import cfg
from octavia.common import constants
from octavia.common.tls_utils import cert_parser
from octavia.i18n import _LW
LOG = logging.getLogger(__name__)
NEUTRON_VERSION = '2.0'
VIP_ROUTE_TABLE = 'vip'
# ip and route commands
CMD_DHCLIENT = "dhclient {0}"
CMD_ADD_IP_ADDR = "ip addr add {0}/24 dev {1}"
CMD_SHOW_IP_ADDR = "ip addr show {0}"
CMD_GREP_LINK_BY_MAC = ("ip link | grep {mac_address} -m 1 -B 1 "
"| awk 'NR==1{{print $2}}'")
CMD_CREATE_VIP_ROUTE_TABLE = (
"su -c 'echo \"1 {0}\" >> /etc/iproute2/rt_tables'"
)
CMD_ADD_ROUTE_TO_TABLE = "ip route add {0} dev {1} table {2}"
CMD_ADD_DEFAULT_ROUTE_TO_TABLE = ("ip route add default via {0} "
"dev {1} table {2}")
CMD_ADD_RULE_FROM_NET_TO_TABLE = "ip rule add from {0} table {1}"
CMD_ADD_RULE_TO_NET_TO_TABLE = "ip rule add to {0} table {1}"
class HaproxyManager(driver_base.AmphoraLoadBalancerDriver):
amp_config = cfg.CONF.haproxy_amphora
def __init__(self):
super(HaproxyManager, self).__init__()
self.amphoraconfig = {}
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.cert_manager = stevedore_driver.DriverManager(
namespace='octavia.cert_manager',
name=cfg.CONF.certificates.cert_manager,
invoke_on_load=True,
).driver
self.jinja = jinja_cfg.JinjaTemplater(
base_amp_path=self.amp_config.base_path,
base_crt_dir=self.amp_config.base_cert_dir,
haproxy_template=self.amp_config.haproxy_template)
def get_logger(self):
return LOG
def update(self, listener, vip):
LOG.debug("Amphora %s haproxy, updating listener %s, vip %s",
self.__class__.__name__, listener.protocol_port,
vip.ip_address)
# Set a path variable to hold where configurations will live
conf_path = '{0}/{1}'.format(self.amp_config.base_path, listener.id)
# Process listener certificate info
certs = self._process_tls_certificates(listener)
# Generate HaProxy configuration from listener object
config = self.jinja.build_config(listener, certs['tls_cert'])
# Build a list of commands to send to the exec method
commands = ['chmod 600 {0}/haproxy.cfg'.format(conf_path),
'haproxy -f {0}/haproxy.cfg -p {0}/{1}.pid -sf '
'$(cat {0}/{1}.pid)'.format(conf_path, listener.id)]
# Exec appropriate commands on all amphorae
self._exec_on_amphorae(
listener.load_balancer.amphorae, commands,
make_dir=conf_path, data=[config],
upload_dir='{0}/haproxy.cfg'.format(conf_path))
def stop(self, listener, vip):
LOG.debug("Amphora %s haproxy, disabling listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
# Exec appropriate commands on all amphorae
self._exec_on_amphorae(listener.load_balancer.amphorae,
['kill -9 $(cat {0}/{1}/{1}.pid)'.format(
self.amp_config.base_path, listener.id)])
def delete(self, listener, vip):
LOG.debug("Amphora %s haproxy, deleting listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
# Define the two operations that need to happen per amphora
stop = 'kill -9 $(cat {0}/{1}/{1}.pid)'.format(
self.amp_config.base_path, listener.id)
delete = 'rm -rf {0}/{1}'.format(self.amp_config.base_path,
listener.id)
# Exec appropriate commands on all amphorae
self._exec_on_amphorae(listener.load_balancer.amphorae, [stop, delete])
def start(self, listener, vip):
LOG.debug("Amphora %s haproxy, enabling listener %s, vip %s",
self.__class__.__name__,
listener.protocol_port, vip.ip_address)
# Define commands to execute on the amphorae
commands = [
'haproxy -f {0}/{1}/haproxy.cfg -p {0}/{1}/{1}.pid'.format(
self.amp_config.base_path, listener.id)]
# Exec appropriate commands on all amphorae
self._exec_on_amphorae(listener.load_balancer.amphorae, commands)
def get_info(self, amphora):
LOG.debug("Amphora %s haproxy, info amphora %s",
self.__class__.__name__, amphora.id)
# info = self.amphora_client.get_info()
# self.amphoraconfig[amphora.id] = (amphora.id, info)
def get_diagnostics(self, amphora):
LOG.debug("Amphora %s haproxy, get diagnostics amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'get_diagnostics')
def finalize_amphora(self, amphora):
LOG.debug("Amphora %s no-op, finalize amphora %s",
self.__class__.__name__, amphora.id)
self.amphoraconfig[amphora.id] = (amphora.id, 'finalize amphora')
def _configure_amp_routes(self, vip_iface, amp_net_config):
subnet = amp_net_config.vip_subnet
command = CMD_CREATE_VIP_ROUTE_TABLE.format(VIP_ROUTE_TABLE)
self._execute_command(command, run_as_root=True)
command = CMD_ADD_ROUTE_TO_TABLE.format(
subnet.cidr, vip_iface, VIP_ROUTE_TABLE)
self._execute_command(command, run_as_root=True)
command = CMD_ADD_DEFAULT_ROUTE_TO_TABLE.format(
subnet.gateway_ip, vip_iface, VIP_ROUTE_TABLE)
self._execute_command(command, run_as_root=True)
command = CMD_ADD_RULE_FROM_NET_TO_TABLE.format(
subnet.cidr, VIP_ROUTE_TABLE)
self._execute_command(command, run_as_root=True)
command = CMD_ADD_RULE_TO_NET_TO_TABLE.format(
subnet.cidr, VIP_ROUTE_TABLE)
self._execute_command(command, run_as_root=True)
def _configure_amp_interface(self, iface, secondary_ip=None):
# just grab the ip from dhcp
command = CMD_DHCLIENT.format(iface)
self._execute_command(command, run_as_root=True)
if secondary_ip:
# add secondary_ip
command = CMD_ADD_IP_ADDR.format(secondary_ip, iface)
self._execute_command(command, run_as_root=True)
# log interface details
command = CMD_SHOW_IP_ADDR.format(iface)
self._execute_command(command)
def post_vip_plug(self, load_balancer, amphorae_network_config):
LOG.debug("Add vip to interface for all amphora on %s",
load_balancer.id)
for amp in load_balancer.amphorae:
if amp.status != constants.DELETED:
# Connect to amphora
self._connect(hostname=amp.lb_network_ip)
mac = amphorae_network_config.get(amp.id).vrrp_port.mac_address
stdout, _ = self._execute_command(
CMD_GREP_LINK_BY_MAC.format(mac_address=mac))
iface = stdout[:-2]
if not iface:
self.client.close()
continue
self._configure_amp_interface(
iface, secondary_ip=load_balancer.vip.ip_address)
self._configure_amp_routes(
iface, amphorae_network_config.get(amp.id))
def post_network_plug(self, amphora, port):
self._connect(hostname=amphora.lb_network_ip)
stdout, _ = self._execute_command(
CMD_GREP_LINK_BY_MAC.format(mac_address=port.mac_address))
iface = stdout[:-2]
if not iface:
self.client.close()
return
self._configure_amp_interface(iface)
self.client.close()
def _execute_command(self, command, run_as_root=False):
if run_as_root and not self._is_root():
command = "sudo {0}".format(command)
_, stdout, stderr = self.client.exec_command(command)
stdout = stdout.read()
stderr = stderr.read()
LOG.debug('Sent command %s', command)
LOG.debug('Returned stdout: %s', stdout)
LOG.debug('Returned stderr: %s', stderr)
return stdout, stderr
def _connect(self, hostname):
for attempts in six.moves.xrange(
self.amp_config.connection_max_retries):
try:
self.client.connect(hostname=hostname,
username=self.amp_config.username,
key_filename=self.amp_config.key_path)
except socket.error:
LOG.warn(_LW("Could not ssh to instance"))
time.sleep(self.amp_config.connection_retry_interval)
if attempts >= self.amp_config.connection_max_retries:
raise exc.TimeOutException()
else:
return
raise exc.UnavailableException()
def _process_tls_certificates(self, listener):
"""Processes TLS data from the listener.
Converts and uploads PEM data to the Amphora API
return TLS_CERT and SNI_CERTS
"""
data = []
certs = cert_parser.load_certificates_data(
self.cert_manager, listener)
sni_containers = certs['sni_certs']
tls_cert = certs['tls_cert']
if certs['tls_cert'] is not None:
data.append(cert_parser.build_pem(tls_cert))
if sni_containers:
for sni_cont in sni_containers:
data.append(cert_parser.build_pem(sni_cont))
if data:
cert_dir = os.path.join(self.amp_config.base_cert_dir, listener.id)
listener_cert = '{0}/{1}.pem'.format(cert_dir, tls_cert.primary_cn)
self._exec_on_amphorae(
listener.load_balancer.amphorae, [
'chmod 600 {0}/*.pem'.format(cert_dir)],
make_dir=cert_dir,
data=data, upload_dir=listener_cert)
return certs
def _exec_on_amphorae(self, amphorae, commands, make_dir=None, data=None,
upload_dir=None):
data = data or []
temps = []
# Write data to temp file to prepare for upload
for datum in data:
temp = tempfile.NamedTemporaryFile(delete=True)
temp.write(datum.encode('ascii'))
temp.flush()
temps.append(temp)
for amp in amphorae:
if amp.status != constants.DELETED:
# Connect to amphora
self._connect(hostname=amp.lb_network_ip)
# Setup for file upload
if make_dir:
mkdir_cmd = 'mkdir -p {0}'.format(make_dir)
self._execute_command(mkdir_cmd, run_as_root=True)
chown_cmd = 'chown -R {0} {1}'.format(
self.amp_config.username, make_dir)
self._execute_command(chown_cmd, run_as_root=True)
# Upload files to location
if temps:
sftp = self.client.open_sftp()
for temp in temps:
sftp.put(temp.name, upload_dir)
# Execute remaining commands
for command in commands:
self._execute_command(command, run_as_root=True)
self.client.close()
# Close the temp file
for temp in temps:
temp.close()
def _is_root(self):
return cfg.CONF.haproxy_amphora.username == 'root'

View File

@ -1,325 +0,0 @@
# Copyright (c) 2015 Rackspace
#
# 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 os
import mock
from oslo_config import cfg
from oslo_utils import uuidutils
import paramiko
from octavia.amphorae.drivers.haproxy.jinja import jinja_cfg
from octavia.amphorae.drivers.haproxy import ssh_driver
from octavia.certificates.manager import cert_mgr
from octavia.common import data_models
from octavia.common import keystone
from octavia.common.tls_utils import cert_parser
from octavia.db import models as models
from octavia.network import data_models as network_models
from octavia.tests.unit import base
from octavia.tests.unit.common.sample_configs import sample_configs
MOCK_NETWORK_ID = '1'
MOCK_SUBNET_ID = '2'
MOCK_PORT_ID = '3'
MOCK_COMPUTE_ID = '4'
MOCK_AMP_ID = '5'
MOCK_IP_ADDRESS = '10.0.0.1'
MOCK_CIDR = '10.0.0.0/24'
class TestSshDriver(base.TestCase):
FAKE_UUID_1 = uuidutils.generate_uuid()
@mock.patch('octavia.common.keystone.get_session',
return_value=mock.MagicMock)
def setUp(self, mock_session):
super(TestSshDriver, self).setUp()
mock.MagicMock(keystone.get_session())
self.driver = ssh_driver.HaproxyManager()
self.listener = sample_configs.sample_listener_tuple()
self.vip = sample_configs.sample_vip_tuple()
self.amphora = models.Amphora()
self.amphora.id = self.FAKE_UUID_1
self.driver.cert_manager = mock.Mock(
spec=cert_mgr.CertManager)
self.driver.client = mock.Mock(spec=paramiko.SSHClient)
self.driver.client.exec_command.return_value = (
mock.Mock(), mock.Mock(), mock.Mock())
self.driver.amp_config = mock.MagicMock()
self.port = network_models.Port(mac_address='123')
def test_update(self):
with mock.patch.object(
self.driver, '_process_tls_certificates') as process_tls_patch:
with mock.patch.object(jinja_cfg.JinjaTemplater,
'build_config') as build_conf:
# Build sample Listener and VIP configs
listener = sample_configs.sample_listener_tuple(tls=True,
sni=True)
vip = sample_configs.sample_vip_tuple()
process_tls_patch.return_value = {
'tls_cert': listener.default_tls_container,
'sni_certs': listener.sni_containers
}
build_conf.return_value = 'sampleConfig'
# Execute driver method
self.driver.update(listener, vip)
# Verify calls
process_tls_patch.assert_called_once_with(listener)
build_conf.assert_called_once_with(
listener, listener.default_tls_container)
self.driver.client.connect.assert_called_once_with(
hostname=listener.load_balancer.amphorae[0].lb_network_ip,
key_filename=self.driver.amp_config.key_path,
username=self.driver.amp_config.username)
self.driver.client.open_sftp.assert_called_once_with()
self.driver.client.open_sftp().put.assert_called_once_with(
mock.ANY, mock.ANY
)
self.driver.client.exec_command.assert_has_calls([
mock.call(mock.ANY),
mock.call(mock.ANY),
mock.call(mock.ANY),
mock.call(mock.ANY)
])
self.driver.client.close.assert_called_once_with()
def test_stop(self):
# Build sample Listener and VIP configs
listener = sample_configs.sample_listener_tuple(
tls=True, sni=True)
vip = sample_configs.sample_vip_tuple()
# Execute driver method
self.driver.start(listener, vip)
self.driver.client.connect.assert_called_once_with(
hostname=listener.load_balancer.amphorae[0].lb_network_ip,
key_filename=self.driver.amp_config.key_path,
username=self.driver.amp_config.username)
self.driver.client.exec_command.assert_called_once_with(
'sudo haproxy -f {0}/{1}/haproxy.cfg -p {0}/{1}/{1}.pid'.format(
self.driver.amp_config.base_path, listener.id))
self.driver.client.close.assert_called_once_with()
def test_start(self):
# Build sample Listener and VIP configs
listener = sample_configs.sample_listener_tuple(
tls=True, sni=True)
vip = sample_configs.sample_vip_tuple()
# Execute driver method
self.driver.start(listener, vip)
self.driver.client.connect.assert_called_once_with(
hostname=listener.load_balancer.amphorae[0].lb_network_ip,
key_filename=self.driver.amp_config.key_path,
username=self.driver.amp_config.username)
self.driver.client.exec_command.assert_called_once_with(
'sudo haproxy -f {0}/{1}/haproxy.cfg -p {0}/{1}/{1}.pid'.format(
self.driver.amp_config.base_path, listener.id))
self.driver.client.close.assert_called_once_with()
def test_delete(self):
# Build sample Listener and VIP configs
listener = sample_configs.sample_listener_tuple(
tls=True, sni=True)
vip = sample_configs.sample_vip_tuple()
# Execute driver method
self.driver.delete(listener, vip)
# Verify call
self.driver.client.connect.assert_called_once_with(
hostname=listener.load_balancer.amphorae[0].lb_network_ip,
key_filename=self.driver.amp_config.key_path,
username=self.driver.amp_config.username)
exec_command_calls = [
mock.call('sudo kill -9 $(cat {0}/sample_listener_id_1'
'/sample_listener_id_1.pid)'
.format(self.driver.amp_config.base_path)),
mock.call('sudo rm -rf {0}/sample_listener_id_1'.format(
self.driver.amp_config.base_path))]
self.driver.client.exec_command.assert_has_calls(exec_command_calls)
self.driver.client.close.assert_called_once_with()
def test_get_info(self):
pass
def test_get_diagnostics(self):
pass
def test_finalize_amphora(self):
pass
def test_process_tls_certificates(self):
listener = sample_configs.sample_listener_tuple(tls=True, sni=True)
with mock.patch.object(cert_parser, 'build_pem') as bp:
with mock.patch.object(cert_parser,
'load_certificates_data') as cd:
with mock.patch.object(cert_parser,
'get_host_names') as cp:
with mock.patch.object(self.driver,
'_exec_on_amphorae') as ea:
self.driver.barbican_client = mock.MagicMock()
cp.return_value = {'cn': 'fakeCN'}
pem = 'imapem'
bp.return_value = pem
tls_cont = data_models.TLSContainer(
primary_cn='fakecn',
certificate='fakecert',
private_key='fakepk')
sni_cont1 = data_models.TLSContainer(
primary_cn='fakecn1',
certificate='fakecert',
private_key='fakepk')
sni_cont2 = data_models.TLSContainer(
primary_cn='fakecn2',
certificate='fakecert',
private_key='fakepk')
cd.return_value = {'tls_cert': tls_cont,
'sni_certs': [sni_cont1, sni_cont2]}
self.driver._process_tls_certificates(listener)
# Ensure upload_cert is called three times
calls_bbq = [mock.call(self.driver.cert_manager,
listener)]
cd.assert_has_calls(calls_bbq)
calls_bp = [
mock.call(tls_cont),
mock.call(sni_cont1),
mock.call(sni_cont2)]
bp.assert_has_calls(calls_bp)
cert_dir = os.path.join(
self.driver.amp_config.base_cert_dir, listener.id)
cmd = 'chmod 600 {base_path}/*.pem'.format(
base_path=cert_dir)
listener_cert = '{0}/fakecn.pem'.format(cert_dir)
ea.assert_has_calls([
mock.call(listener.load_balancer.amphorae,
[cmd], make_dir=cert_dir,
data=[pem, pem, pem],
upload_dir=listener_cert)])
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
def test_post_vip_plug_no_down_links(self, exec_command):
amps = [data_models.Amphora(id=MOCK_AMP_ID, compute_id=MOCK_COMPUTE_ID,
lb_network_ip=MOCK_IP_ADDRESS)]
vip = data_models.Vip(ip_address=MOCK_IP_ADDRESS)
lb = data_models.LoadBalancer(amphorae=amps, vip=vip)
amphorae_net_config = {amps[0].id: network_models.AmphoraNetworkConfig(
amphora=amps[0],
vrrp_port=self.port
)}
exec_command.return_value = ('', '')
self.driver.post_vip_plug(lb, amphorae_net_config)
exec_command.assert_called_once_with(
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
def test_post_vip_plug(self, exec_command):
amps = [data_models.Amphora(id=MOCK_AMP_ID, compute_id=MOCK_COMPUTE_ID,
lb_network_ip=MOCK_IP_ADDRESS)]
vip = data_models.Vip(ip_address=MOCK_IP_ADDRESS)
lb = data_models.LoadBalancer(amphorae=amps, vip=vip)
vip_subnet = network_models.Subnet(id=MOCK_SUBNET_ID,
gateway_ip=MOCK_IP_ADDRESS,
cidr=MOCK_CIDR)
vip_port = network_models.Port(id=MOCK_PORT_ID,
device_id=MOCK_COMPUTE_ID)
amphorae_net_config = {amps[0].id: network_models.AmphoraNetworkConfig(
amphora=amps[0],
vip_subnet=vip_subnet,
vip_port=vip_port,
vrrp_port=self.port
)}
iface = 'eth1'
exec_command.return_value = ('{0}: '.format(iface), '')
self.driver.post_vip_plug(lb, amphorae_net_config)
grep_call = mock.call(
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
dhclient_call = mock.call(ssh_driver.CMD_DHCLIENT.format(iface),
run_as_root=True)
add_ip_call = mock.call(ssh_driver.CMD_ADD_IP_ADDR.format(
MOCK_IP_ADDRESS, iface), run_as_root=True)
show_ip_call = mock.call(ssh_driver.CMD_SHOW_IP_ADDR.format(iface))
create_vip_table_call = mock.call(
ssh_driver.CMD_CREATE_VIP_ROUTE_TABLE.format(
ssh_driver.VIP_ROUTE_TABLE),
run_as_root=True
)
add_route_call = mock.call(
ssh_driver.CMD_ADD_ROUTE_TO_TABLE.format(
MOCK_CIDR, iface, ssh_driver.VIP_ROUTE_TABLE),
run_as_root=True
)
add_default_route_call = mock.call(
ssh_driver.CMD_ADD_DEFAULT_ROUTE_TO_TABLE.format(
MOCK_IP_ADDRESS, iface, ssh_driver.VIP_ROUTE_TABLE),
run_as_root=True
)
add_rule_from_call = mock.call(
ssh_driver.CMD_ADD_RULE_FROM_NET_TO_TABLE.format(
MOCK_CIDR, ssh_driver.VIP_ROUTE_TABLE),
run_as_root=True
)
add_rule_to_call = mock.call(
ssh_driver.CMD_ADD_RULE_TO_NET_TO_TABLE.format(
MOCK_CIDR, ssh_driver.VIP_ROUTE_TABLE),
run_as_root=True
)
exec_command.assert_has_calls([grep_call, dhclient_call, add_ip_call,
show_ip_call, create_vip_table_call,
add_route_call, add_default_route_call,
add_rule_from_call, add_rule_to_call])
self.assertEqual(9, exec_command.call_count)
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
def test_post_network_plug_no_down_links(self, exec_command):
amp = data_models.Amphora(id=MOCK_AMP_ID, compute_id=MOCK_COMPUTE_ID,
lb_network_ip=MOCK_IP_ADDRESS)
exec_command.return_value = ('', '')
self.driver.post_network_plug(amp, self.port)
exec_command.assert_called_once_with(
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
@mock.patch.object(ssh_driver.HaproxyManager, '_execute_command')
def test_post_network_plug(self, exec_command):
amp = data_models.Amphora(id=MOCK_AMP_ID, compute_id=MOCK_COMPUTE_ID,
lb_network_ip=MOCK_IP_ADDRESS)
iface = 'eth1'
exec_command.return_value = ('{0}: '.format(iface), '')
self.driver.post_network_plug(amp, self.port)
grep_call = mock.call(
ssh_driver.CMD_GREP_LINK_BY_MAC.format(mac_address='123'))
dhclient_call = mock.call(ssh_driver.CMD_DHCLIENT.format(iface),
run_as_root=True)
show_ip_call = mock.call(ssh_driver.CMD_SHOW_IP_ADDR.format(iface))
exec_command.assert_has_calls([grep_call, dhclient_call, show_ip_call])
self.assertEqual(3, exec_command.call_count)
def test_is_root(self):
cfg.CONF.set_override('username', 'root', group='haproxy_amphora')
self.assertTrue(self.driver._is_root())
cfg.CONF.set_override('username', 'blah', group='haproxy_amphora')
self.assertFalse(self.driver._is_root())

View File

@ -28,7 +28,7 @@ AUTH_VERSION = '2'
class TestAmphoraFlows(base.TestCase):
def setUp(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_ssh_driver',
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker')
self.AmpFlow = amphora_flows.AmphoraFlows()
conf = oslo_fixture.Config(cfg.CONF)
@ -45,8 +45,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertEqual(4, len(amp_flow.provides))
self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(0, len(amp_flow.requires))
def test_get_create_amphora_flow_cert(self):
@ -66,7 +67,7 @@ class TestAmphoraFlows(base.TestCase):
self.assertEqual(0, len(amp_flow.requires))
def test_get_create_amphora_for_lb_flow(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_ssh_driver',
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',
group='controller_worker')
amp_flow = self.AmpFlow._get_create_amp_for_lb_subflow(
@ -80,8 +81,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertEqual(4, len(amp_flow.provides))
self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(1, len(amp_flow.requires))
def test_get_cert_create_amphora_for_lb_flow(self):
@ -205,16 +207,21 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.FAILED_AMPHORA, amp_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMP_DATA, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(11, len(amp_flow.provides))
self.assertEqual(12, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role=constants.ROLE_MASTER)
@ -222,16 +229,21 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.FAILED_AMPHORA, amp_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMP_DATA, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(11, len(amp_flow.provides))
self.assertEqual(12, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role=constants.ROLE_BACKUP)
@ -239,16 +251,21 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.FAILED_AMPHORA, amp_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMP_DATA, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(11, len(amp_flow.provides))
self.assertEqual(12, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role='BOGUSROLE')
@ -256,16 +273,21 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.FAILED_AMPHORA, amp_flow.requires)
self.assertIn(constants.LOADBALANCER_ID, amp_flow.requires)
self.assertIn(constants.AMP_DATA, amp_flow.provides)
self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.AMPHORAE_NETWORK_CONFIG, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(11, len(amp_flow.provides))
self.assertEqual(12, len(amp_flow.provides))
def test_cert_rotate_amphora_flow(self):
cfg.CONF.set_override('amphora_driver', 'amphora_haproxy_rest_driver',

View File

@ -49,7 +49,6 @@ octavia.api.handlers =
octavia.amphora.drivers =
amphora_noop_driver = octavia.amphorae.drivers.noop_driver.driver:NoopAmphoraLoadBalancerDriver
amphora_haproxy_rest_driver = octavia.amphorae.drivers.haproxy.rest_api_driver:HaproxyAmphoraLoadBalancerDriver
amphora_haproxy_ssh_driver = octavia.amphorae.drivers.haproxy.ssh_driver:HaproxyManager
octavia.controller.queues =
noop_event_streamer = octavia.controller.queue.event_queue:EventStreamerNoop
queue_event_streamer = octavia.controller.queue.event_queue:EventStreamerNeutron

View File

@ -89,7 +89,7 @@ The Active/Standby loadbalancers require the following high level changes:
the same listeners, and pools configuration. Note: topology is a property
of a load balancer and not of one of its amphorae.
* Extend the amphora driver interface, the amphora REST/SSH drivers, and Jinja
* Extend the amphora driver interface, the amphora REST driver, and Jinja
configuration templates for the newly introduced VRRP service [4].
* Develop a Keepalived driver.