Add support for Octavia resources in Heat
Octavia is already an official LBaaS solution for Openstack (https://governance.openstack.org/tc/reference/projects/octavia.html) and will deprecate the neutron-lbaas extension starting from Queens release. For deployment support Octavia service for load balancing functionlity, Octavia related resources instead of LBaaS should be used in Heat template. Tested in my DevStack environment. Change-Id: Icc45e0a126c648fbcba4ebcd1bb258d60957f2d6 Closes-Bug: #1748577
This commit is contained in:
parent
21c87f35a0
commit
3c8edd4d88
@ -367,6 +367,10 @@ class RegionsListFailed(MagnumException):
|
||||
message = _("Failed to list regions.")
|
||||
|
||||
|
||||
class ServicesListFailed(MagnumException):
|
||||
message = _("Failed to list services.")
|
||||
|
||||
|
||||
class TrusteeOrTrustToClusterFailed(MagnumException):
|
||||
message = _("Failed to create trustee or trust for Cluster: "
|
||||
"%(cluster_uuid)s")
|
||||
|
@ -291,3 +291,32 @@ class KeystoneClientV3(object):
|
||||
'region_name_list': '/'.join(
|
||||
region_list + ['unspecified'])})
|
||||
return region_name
|
||||
|
||||
|
||||
def is_octavia_enabled():
|
||||
"""Check if Octavia service is deployed in the cloud.
|
||||
|
||||
Octavia is already an official LBaaS solution for Openstack
|
||||
(https://governance.openstack.org/tc/reference/projects/octavia.html) and
|
||||
will deprecate the neutron-lbaas extension starting from Queens release.
|
||||
|
||||
We use Octavia instead of Neutron LBaaS API for load balancing
|
||||
functionality for k8s cluster if Octavia service is deployed and enabled
|
||||
in the cloud.
|
||||
"""
|
||||
# Put the import here to avoid circular importing.
|
||||
from magnum.common import context
|
||||
admin_context = context.make_admin_context()
|
||||
keystone = KeystoneClientV3(admin_context)
|
||||
|
||||
try:
|
||||
octavia_svc = keystone.client.services.list(type='load-balancer')
|
||||
except Exception:
|
||||
LOG.exception('Failed to list services')
|
||||
raise exception.ServicesListFailed()
|
||||
|
||||
# Always assume there is only one load balancing service configured.
|
||||
if octavia_svc and octavia_svc[0].enabled:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -0,0 +1,14 @@
|
||||
# Environment file to enable LBaaS in a cluster by mapping
|
||||
# LBaaS-related resource types to the real Octavia resource types.
|
||||
resource_registry:
|
||||
"Magnum::ApiGatewaySwitcher": ../fragments/api_gateway_switcher_pool.yaml
|
||||
|
||||
# Cluster template
|
||||
"Magnum::Optional::Neutron::LBaaS::LoadBalancer": "OS::Octavia::LoadBalancer"
|
||||
"Magnum::Optional::Neutron::LBaaS::Listener": "OS::Octavia::Listener"
|
||||
"Magnum::Optional::Neutron::LBaaS::Pool": "OS::Octavia::Pool"
|
||||
"Magnum::Optional::Neutron::LBaaS::HealthMonitor": "OS::Octavia::HealthMonitor"
|
||||
"Magnum::Optional::Neutron::LBaaS::FloatingIP": "OS::Neutron::FloatingIP"
|
||||
|
||||
# Master node template
|
||||
"Magnum::Optional::Neutron::LBaaS::PoolMember": "OS::Octavia::PoolMember"
|
@ -20,6 +20,7 @@ import six
|
||||
|
||||
from magnum.common import clients
|
||||
from magnum.common import exception
|
||||
from magnum.common import keystone
|
||||
from magnum.common import utils
|
||||
import magnum.conf
|
||||
|
||||
@ -328,7 +329,10 @@ class BaseTemplateDefinition(TemplateDefinition):
|
||||
|
||||
def add_lb_env_file(env_files, cluster_template):
|
||||
if cluster_template.master_lb_enabled:
|
||||
env_files.append(COMMON_ENV_PATH + 'with_master_lb.yaml')
|
||||
if keystone.is_octavia_enabled():
|
||||
env_files.append(COMMON_ENV_PATH + 'with_master_lb_octavia.yaml')
|
||||
else:
|
||||
env_files.append(COMMON_ENV_PATH + 'with_master_lb.yaml')
|
||||
else:
|
||||
env_files.append(COMMON_ENV_PATH + 'no_master_lb.yaml')
|
||||
|
||||
|
@ -207,8 +207,10 @@ class TestClusterConductorWithMesos(base.TestCase):
|
||||
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
def test_extract_template_definition_with_lb(
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_with_lb_neutron(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid):
|
||||
self.cluster_template_dict['master_lb_enabled'] = True
|
||||
@ -219,6 +221,8 @@ class TestClusterConductorWithMesos(base.TestCase):
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
mock_driver.return_value = mesos_dr.Driver()
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = []
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
@ -266,8 +270,78 @@ class TestClusterConductorWithMesos(base.TestCase):
|
||||
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_with_lb_octavia(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid):
|
||||
self.cluster_template_dict['master_lb_enabled'] = True
|
||||
cluster_template = objects.ClusterTemplate(
|
||||
self.context, **self.cluster_template_dict)
|
||||
mock_objects_cluster_template_get_by_uuid.return_value = \
|
||||
cluster_template
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
mock_driver.return_value = mesos_dr.Driver()
|
||||
|
||||
class Service(object):
|
||||
def __init__(self):
|
||||
self.enabled = True
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = [Service()]
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
cluster)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network': 'external_network_id',
|
||||
'fixed_network': 'fixed_network',
|
||||
'fixed_subnet': 'fixed_subnet',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'server_image': 'image_id',
|
||||
'master_flavor': 'master_flavor_id',
|
||||
'slave_flavor': 'flavor_id',
|
||||
'number_of_slaves': 1,
|
||||
'number_of_masters': 1,
|
||||
'http_proxy': 'http_proxy',
|
||||
'https_proxy': 'https_proxy',
|
||||
'no_proxy': 'no_proxy',
|
||||
'cluster_name': 'cluster1',
|
||||
'trustee_domain_id': self.mock_keystone.trustee_domain_id,
|
||||
'trustee_username': 'fake_trustee',
|
||||
'trustee_password': 'fake_trustee_password',
|
||||
'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656',
|
||||
'trust_id': '',
|
||||
'volume_driver': 'volume_driver',
|
||||
'auth_url': 'http://192.168.10.10:5000/v3',
|
||||
'region_name': self.mock_osc.cinder_region_name.return_value,
|
||||
'username': 'mesos_user',
|
||||
'tenant_name': 'admin',
|
||||
'domain_name': 'domainname',
|
||||
'rexray_preempt': 'False',
|
||||
'mesos_slave_executor_env_variables': '{}',
|
||||
'mesos_slave_isolation': 'docker/runtime,filesystem/linux',
|
||||
'mesos_slave_work_dir': '/tmp/mesos/slave',
|
||||
'mesos_slave_image_providers': 'docker',
|
||||
'verify_ca': True,
|
||||
'openstack_ca': '',
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual(
|
||||
['../../common/templates/environments/no_private_network.yaml',
|
||||
'../../common/templates/environments/with_master_lb_octavia.yaml'
|
||||
],
|
||||
env_files)
|
||||
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_multi_master(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid):
|
||||
self.cluster_template_dict['master_lb_enabled'] = True
|
||||
@ -279,6 +353,8 @@ class TestClusterConductorWithMesos(base.TestCase):
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
mock_driver.return_value = mesos_dr.Driver()
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = []
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
|
@ -330,8 +330,10 @@ class TestClusterConductorWithSwarm(base.TestCase):
|
||||
@patch('requests.get')
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
def test_extract_template_definition_with_lb(
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_with_lb_neutron(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid,
|
||||
mock_get):
|
||||
@ -348,6 +350,8 @@ class TestClusterConductorWithSwarm(base.TestCase):
|
||||
mock_driver.return_value = swarm_dr.Driver()
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = []
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
@ -403,8 +407,92 @@ class TestClusterConductorWithSwarm(base.TestCase):
|
||||
@patch('requests.get')
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_with_lb_octavia(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid,
|
||||
mock_get):
|
||||
self.cluster_template_dict['master_lb_enabled'] = True
|
||||
cluster_template = objects.ClusterTemplate(
|
||||
self.context, **self.cluster_template_dict)
|
||||
mock_objects_cluster_template_get_by_uuid.return_value = \
|
||||
cluster_template
|
||||
expected_result = str('{"action":"get","node":{"key":"test","value":'
|
||||
'"1","modifiedIndex":10,"createdIndex":10}}')
|
||||
mock_resp = mock.MagicMock()
|
||||
mock_resp.text = expected_result
|
||||
mock_get.return_value = mock_resp
|
||||
mock_driver.return_value = swarm_dr.Driver()
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
|
||||
class Service(object):
|
||||
def __init__(self):
|
||||
self.enabled = True
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = [Service()]
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
cluster)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
'external_network': 'external_network_id',
|
||||
'fixed_network': 'fixed_network',
|
||||
'fixed_subnet': 'fixed_subnet',
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
'server_image': 'image_id',
|
||||
'master_flavor': 'master_flavor_id',
|
||||
'node_flavor': 'flavor_id',
|
||||
'number_of_masters': 1,
|
||||
'number_of_nodes': 1,
|
||||
'docker_volume_size': 20,
|
||||
'docker_storage_driver': 'devicemapper',
|
||||
'discovery_url': 'https://discovery.test.io/123456789',
|
||||
'http_proxy': 'http_proxy',
|
||||
'https_proxy': 'https_proxy',
|
||||
'no_proxy': 'no_proxy',
|
||||
'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52',
|
||||
'magnum_url': self.mock_osc.magnum_url.return_value,
|
||||
'tls_disabled': False,
|
||||
'registry_enabled': False,
|
||||
'network_driver': 'network_driver',
|
||||
'flannel_network_cidr': '10.101.0.0/16',
|
||||
'flannel_network_subnetlen': '26',
|
||||
'flannel_backend': 'vxlan',
|
||||
'trustee_domain_id': self.mock_keystone.trustee_domain_id,
|
||||
'trustee_username': 'fake_trustee',
|
||||
'trustee_password': 'fake_trustee_password',
|
||||
'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656',
|
||||
'trust_id': 'bd11efc5-d4e2-4dac-bbce-25e348ddf7de',
|
||||
'auth_url': 'http://192.168.10.10:5000/v3',
|
||||
'swarm_version': 'fake-version',
|
||||
'swarm_strategy': u'spread',
|
||||
'volume_driver': 'rexray',
|
||||
'rexray_preempt': 'False',
|
||||
'docker_volume_type': 'lvmdriver-1',
|
||||
'verify_ca': True,
|
||||
'openstack_ca': '',
|
||||
'nodes_affinity_policy': 'soft-anti-affinity'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual(
|
||||
['../../common/templates/environments/no_private_network.yaml',
|
||||
'../../common/templates/environments/with_volume.yaml',
|
||||
'../../common/templates/environments/with_master_lb_octavia.yaml'
|
||||
],
|
||||
env_files)
|
||||
|
||||
@patch('requests.get')
|
||||
@patch('magnum.objects.ClusterTemplate.get_by_uuid')
|
||||
@patch('magnum.drivers.common.driver.Driver.get_driver')
|
||||
@patch('magnum.common.keystone.KeystoneClientV3')
|
||||
def test_extract_template_definition_multi_master(
|
||||
self,
|
||||
mock_kc,
|
||||
mock_driver,
|
||||
mock_objects_cluster_template_get_by_uuid,
|
||||
mock_get):
|
||||
@ -422,6 +510,8 @@ class TestClusterConductorWithSwarm(base.TestCase):
|
||||
mock_driver.return_value = swarm_dr.Driver()
|
||||
cluster = objects.Cluster(self.context, **self.cluster_dict)
|
||||
|
||||
mock_kc.return_value.client.services.list.return_value = []
|
||||
|
||||
(template_path,
|
||||
definition,
|
||||
env_files) = mock_driver()._extract_template_definition(self.context,
|
||||
|
Loading…
x
Reference in New Issue
Block a user