Merge "Implement a load balancer resource using new neutron pool"
This commit is contained in:
commit
4c1bfe32a5
@ -14,8 +14,11 @@
|
||||
# under the License.
|
||||
|
||||
from heat.common import exception
|
||||
from heat.db.sqlalchemy import api as db_api
|
||||
from heat.engine import clients
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.engine.resources import nova_utils
|
||||
from heat.engine.resources.neutron import neutron
|
||||
|
||||
if clients.neutronclient is not None:
|
||||
@ -186,7 +189,7 @@ class Pool(neutron.NeutronResource):
|
||||
client = self.neutron()
|
||||
monitors = set(prop_diff.pop('monitors', []))
|
||||
if monitors:
|
||||
old_monitors = set(self.t['Properties'].get('monitors', []))
|
||||
old_monitors = set(self.t['Properties']['monitors'])
|
||||
for monitor in old_monitors - monitors:
|
||||
client.disassociate_health_monitor(
|
||||
self.resource_id, {'health_monitor': {'id': monitor}})
|
||||
@ -237,6 +240,79 @@ class Pool(neutron.NeutronResource):
|
||||
self._delete_pool()
|
||||
|
||||
|
||||
class LoadBalancer(resource.Resource):
|
||||
"""
|
||||
A resource to link a neutron pool with servers.
|
||||
"""
|
||||
|
||||
properties_schema = {
|
||||
'pool_id': {
|
||||
'Type': 'String', 'Required': True,
|
||||
'Description': _('The ID of the load balancing pool')},
|
||||
'protocol_port': {
|
||||
'Type': 'Integer', 'Required': True,
|
||||
'Description': _('Port number on which the servers are '
|
||||
'running on the members')},
|
||||
'members': {
|
||||
'Type': 'List',
|
||||
'Description': _('The list of Nova server IDs load balanced')},
|
||||
}
|
||||
|
||||
update_allowed_keys = ('Properties',)
|
||||
|
||||
update_allowed_properties = ('members',)
|
||||
|
||||
def handle_create(self):
|
||||
pool = self.properties['pool_id']
|
||||
client = self.neutron()
|
||||
nova_client = self.nova()
|
||||
protocol_port = self.properties['protocol_port']
|
||||
for member in self.properties['members']:
|
||||
address = nova_utils.server_to_ipaddress(nova_client, member)
|
||||
lb_member = client.create_member({
|
||||
'member': {
|
||||
'pool_id': pool,
|
||||
'address': address,
|
||||
'protocol_port': protocol_port}})['member']
|
||||
db_api.resource_data_set(self, member, lb_member['id'])
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if 'members' in prop_diff:
|
||||
members = set(prop_diff['members'])
|
||||
old_members = set(self.t['Properties']['members'])
|
||||
client = self.neutron()
|
||||
for member in old_members - members:
|
||||
member_id = db_api.resource_data_get(self, member)
|
||||
try:
|
||||
client.delete_member(member_id)
|
||||
except NeutronClientException as ex:
|
||||
if ex.status_code != 404:
|
||||
raise ex
|
||||
db_api.resource_data_delete(self, member)
|
||||
pool = self.properties['pool_id']
|
||||
nova_client = self.nova()
|
||||
protocol_port = self.properties['protocol_port']
|
||||
for member in members - old_members:
|
||||
address = nova_utils.server_to_ipaddress(nova_client, member)
|
||||
lb_member = client.create_member({
|
||||
'member': {
|
||||
'pool_id': pool,
|
||||
'address': address,
|
||||
'protocol_port': protocol_port}})['member']
|
||||
db_api.resource_data_set(self, member, lb_member['id'])
|
||||
|
||||
def handle_delete(self):
|
||||
client = self.neutron()
|
||||
for member in self.properties['members']:
|
||||
member_id = db_api.resource_data_get(self, member)
|
||||
try:
|
||||
client.delete_member(member_id)
|
||||
except NeutronClientException as ex:
|
||||
if ex.status_code != 404:
|
||||
raise ex
|
||||
db_api.resource_data_delete(self, member)
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
if clients.neutronclient is None:
|
||||
return {}
|
||||
@ -244,4 +320,5 @@ def resource_mapping():
|
||||
return {
|
||||
'OS::Neutron::HealthMonitor': HealthMonitor,
|
||||
'OS::Neutron::Pool': Pool,
|
||||
'OS::Neutron::LoadBalancer': LoadBalancer,
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ from heat.openstack.common.importutils import try_import
|
||||
from heat.tests import fakes
|
||||
from heat.tests import utils
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests.v1_1 import fakes as nova_fakes
|
||||
|
||||
neutronclient = try_import('neutronclient.v2_0.client')
|
||||
|
||||
@ -68,6 +69,24 @@ pool_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
lb_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Template to test load balancer resources",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"lb": {
|
||||
"Type": "OS::Neutron::LoadBalancer",
|
||||
"Properties": {
|
||||
"protocol_port": 8080,
|
||||
"pool_id": "pool123",
|
||||
"members": ["1234"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
@skipIf(neutronclient is None, 'neutronclient unavailable')
|
||||
class HealthMonitorTest(HeatTestCase):
|
||||
@ -517,3 +536,93 @@ class PoolTest(HeatTestCase):
|
||||
self.assertEqual(None, rsrc.update(update_template))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
@skipIf(neutronclient is None, 'neutronclient unavailable')
|
||||
class LoadBalancerTest(HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LoadBalancerTest, self).setUp()
|
||||
self.fc = nova_fakes.FakeClient()
|
||||
self.m.StubOutWithMock(neutronclient.Client, 'create_member')
|
||||
self.m.StubOutWithMock(neutronclient.Client, 'delete_member')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
|
||||
utils.setup_dummy_db()
|
||||
|
||||
def create_load_balancer(self):
|
||||
clients.OpenStackClients.keystone().AndReturn(
|
||||
fakes.FakeKeystoneClient())
|
||||
clients.OpenStackClients.nova("compute").MultipleTimes().AndReturn(
|
||||
self.fc)
|
||||
neutronclient.Client.create_member({
|
||||
'member': {
|
||||
'pool_id': 'pool123', 'protocol_port': 8080,
|
||||
'address': '1.2.3.4'}}
|
||||
).AndReturn({'member': {'id': 'member5678'}})
|
||||
snippet = template_format.parse(lb_template)
|
||||
stack = utils.parse_stack(snippet)
|
||||
return loadbalancer.LoadBalancer(
|
||||
'lb', snippet['Resources']['lb'], stack)
|
||||
|
||||
def test_create(self):
|
||||
rsrc = self.create_load_balancer()
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update(self):
|
||||
rsrc = self.create_load_balancer()
|
||||
neutronclient.Client.delete_member(u'member5678')
|
||||
neutronclient.Client.create_member({
|
||||
'member': {
|
||||
'pool_id': 'pool123', 'protocol_port': 8080,
|
||||
'address': '4.5.6.7'}}
|
||||
).AndReturn({'member': {'id': 'memberxyz'}})
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
|
||||
update_template = copy.deepcopy(rsrc.t)
|
||||
update_template['Properties']['members'] = ['5678']
|
||||
|
||||
self.assertEqual(None, rsrc.update(update_template))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_update_missing_member(self):
|
||||
rsrc = self.create_load_balancer()
|
||||
neutronclient.Client.delete_member(u'member5678').AndRaise(
|
||||
loadbalancer.NeutronClientException(status_code=404))
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
|
||||
update_template = copy.deepcopy(rsrc.t)
|
||||
update_template['Properties']['members'] = []
|
||||
|
||||
self.assertEqual(None, rsrc.update(update_template))
|
||||
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_delete(self):
|
||||
rsrc = self.create_load_balancer()
|
||||
neutronclient.Client.delete_member(u'member5678')
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_delete_missing_member(self):
|
||||
rsrc = self.create_load_balancer()
|
||||
neutronclient.Client.delete_member(u'member5678').AndRaise(
|
||||
loadbalancer.NeutronClientException(status_code=404))
|
||||
|
||||
self.m.ReplayAll()
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
|
||||
self.m.VerifyAll()
|
||||
|
Loading…
Reference in New Issue
Block a user