Add OS::Neutron::RouterL3Agent for router.py
This resource supports part of "agent management and scheduler extensions" in Neutron. It associates router with l3-agent. see related-bug: #1245366 Closes-bug: #1259845 Change-Id: I781645e62beb4fe60e064d61ad298834e551142d
This commit is contained in:
parent
582cf8f047
commit
54f3f1d60a
@ -16,6 +16,7 @@
|
||||
from heat.common import exception
|
||||
from heat.engine import clients
|
||||
from heat.engine.resources.neutron import neutron
|
||||
from heat.engine import properties
|
||||
from heat.engine import scheduler
|
||||
|
||||
if clients.neutronclient is not None:
|
||||
@ -93,6 +94,16 @@ class RouterInterface(neutron.NeutronResource):
|
||||
'Description': _('The port id, either '
|
||||
'subnet_id or port_id should be specified.')}}
|
||||
|
||||
def add_dependencies(self, deps):
|
||||
super(RouterInterface, self).add_dependencies(deps)
|
||||
# depend on any RouterL3agents in this template with the same router_id
|
||||
# as this router_id.
|
||||
for resource in self.stack.itervalues():
|
||||
if (resource.has_interface('OS::Neutron::RouterL3Agent') and
|
||||
resource.properties.get('router_id') ==
|
||||
self.properties.get('router_id')):
|
||||
deps += (self, resource)
|
||||
|
||||
def validate(self):
|
||||
'''
|
||||
Validate any of the provided params
|
||||
@ -157,6 +168,12 @@ class RouterGateway(neutron.NeutronResource):
|
||||
resource.properties.get('network_id') ==
|
||||
self.properties.get('network_id')):
|
||||
deps += (self, resource)
|
||||
# depend on any RouterL3agents in this template with the same
|
||||
# router_id as this router_id.
|
||||
elif (resource.has_interface('OS::Neutron::RouterL3Agent') and
|
||||
resource.properties.get('router_id') ==
|
||||
self.properties.get('router_id')):
|
||||
deps += (self, resource)
|
||||
|
||||
def handle_create(self):
|
||||
router_id = self.properties.get('router_id')
|
||||
@ -181,6 +198,50 @@ class RouterGateway(neutron.NeutronResource):
|
||||
raise ex
|
||||
|
||||
|
||||
class RouterL3Agent(neutron.NeutronResource):
|
||||
properties_schema = {
|
||||
'router_id': properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
description=_('The ID of the router you want to be scheduled by '
|
||||
'the l3_agent. Note that the default policy setting '
|
||||
'in Neutron restricts usage of this property to '
|
||||
'administrative users only.'),
|
||||
required=True
|
||||
),
|
||||
'l3_agent_id': properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
description=_('The ID of the l3-agent to schedule the router. '
|
||||
'Note that the default policy setting in Neutron '
|
||||
'restricts usage of this property to administrative '
|
||||
'users only.'),
|
||||
required=True
|
||||
)
|
||||
}
|
||||
|
||||
def handle_create(self):
|
||||
router_id = self.properties['router_id']
|
||||
l3_agent_id = self.properties['l3_agent_id']
|
||||
self.neutron().add_router_to_l3_agent(
|
||||
l3_agent_id, {'router_id': router_id})
|
||||
self.resource_id_set('%(rtr)s:%(agt)s' %
|
||||
{'rtr': router_id, 'agt': l3_agent_id})
|
||||
|
||||
def handle_delete(self):
|
||||
if not self.resource_id:
|
||||
return
|
||||
client = self.neutron()
|
||||
router_id, l3_agent_id = self.resource_id.split(':')
|
||||
try:
|
||||
client.remove_router_from_l3_agent(
|
||||
l3_agent_id, router_id)
|
||||
except NeutronClientException as ex:
|
||||
# assume 2 patterns about status_code following:
|
||||
# 404: the router or agent is already gone
|
||||
# 409: the router isn't scheduled by the l3_agent
|
||||
if ex.status_code not in (404, 409):
|
||||
raise ex
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
if clients.neutronclient is None:
|
||||
return {}
|
||||
@ -189,4 +250,5 @@ def resource_mapping():
|
||||
'OS::Neutron::Router': Router,
|
||||
'OS::Neutron::RouterInterface': RouterInterface,
|
||||
'OS::Neutron::RouterGateway': RouterGateway,
|
||||
'OS::Neutron::RouterL3Agent': RouterL3Agent,
|
||||
}
|
||||
|
@ -124,6 +124,36 @@ neutron_dhcp_agent_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
neutron_l3_agent_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Template to test Neutron resources",
|
||||
"Resources" : {
|
||||
"router_l3_agent": {
|
||||
"Type": "OS::Neutron::RouterL3Agent",
|
||||
"Properties": {
|
||||
"router_id": "2b0347ab-9e42-434f-8249-702eda4ce7a6",
|
||||
"l3_agent_id": "5dab1619-9bb0-4e6f-9725-c5e2bfdec434"
|
||||
}
|
||||
},
|
||||
"router_interface": {
|
||||
"Type": "OS::Neutron::RouterInterface",
|
||||
"Properties": {
|
||||
"router_id": "2b0347ab-9e42-434f-8249-702eda4ce7a6",
|
||||
"subnet_id": "10c69b87-6322-4e5f-9616-fb18ad6547b4"
|
||||
}
|
||||
},
|
||||
"router_gateway": {
|
||||
"Type": "OS::Neutron::RouterGateway",
|
||||
"Properties": {
|
||||
"router_id": "2b0347ab-9e42-434f-8249-702eda4ce7a6",
|
||||
"network_id": "b3ae63e2-a17b-4a1e-823b-5a082c562725"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
neutron_floating_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
@ -770,6 +800,10 @@ class NeutronRouterTest(HeatTestCase):
|
||||
self.m.StubOutWithMock(neutronclient.Client, 'remove_interface_router')
|
||||
self.m.StubOutWithMock(neutronclient.Client, 'add_gateway_router')
|
||||
self.m.StubOutWithMock(neutronclient.Client, 'remove_gateway_router')
|
||||
self.m.StubOutWithMock(neutronclient.Client,
|
||||
'add_router_to_l3_agent')
|
||||
self.m.StubOutWithMock(neutronclient.Client,
|
||||
'remove_router_from_l3_agent')
|
||||
self.m.StubOutWithMock(router.neutronV20,
|
||||
'find_resourceid_by_name_or_id')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
||||
@ -1083,6 +1117,103 @@ class NeutronRouterTest(HeatTestCase):
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_router_l3_agent(self):
|
||||
clients.OpenStackClients.keystone().AndReturn(
|
||||
fakes.FakeKeystoneClient())
|
||||
|
||||
neutronclient.Client.add_router_to_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
{'router_id': u'2b0347ab-9e42-434f-8249-702eda4ce7a6'}
|
||||
).AndReturn(None)
|
||||
|
||||
neutronclient.Client.remove_router_from_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
u'2b0347ab-9e42-434f-8249-702eda4ce7a6'
|
||||
).AndReturn(None)
|
||||
|
||||
neutronclient.Client.remove_router_from_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
u'2b0347ab-9e42-434f-8249-702eda4ce7a6'
|
||||
).AndRaise(qe.NeutronClientException(status_code=404))
|
||||
|
||||
self.m.ReplayAll()
|
||||
t = template_format.parse(neutron_l3_agent_template)
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = router.RouterL3Agent('test_router_l3_agent',
|
||||
t['Resources']['router_l3_agent'], stack)
|
||||
|
||||
# assert the implicit dependency between the l3_agent and the
|
||||
# interface, and the l3_agent and the gateway
|
||||
deps = stack.dependencies[stack['router_l3_agent']]
|
||||
self.assertIn(stack['router_gateway'], deps)
|
||||
self.assertIn(stack['router_interface'], deps)
|
||||
|
||||
# assert the implicit dependency between the router and the interface
|
||||
deps = stack.dependencies[stack['router_interface']]
|
||||
self.assertIn(stack['router_gateway'], deps)
|
||||
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
|
||||
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
|
||||
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_router_l3_agent_create_failed(self):
|
||||
clients.OpenStackClients.keystone().AndReturn(
|
||||
fakes.FakeKeystoneClient())
|
||||
|
||||
neutronclient.Client.add_router_to_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
{'router_id': u'2b0347ab-9e42-434f-8249-702eda4ce7a6'}
|
||||
).AndRaise(qe.NeutronClientException(status_code=500))
|
||||
|
||||
self.m.ReplayAll()
|
||||
t = template_format.parse(neutron_l3_agent_template)
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = router.RouterL3Agent('test_router_l3_agent',
|
||||
t['Resources']['router_l3_agent'], stack)
|
||||
error = self.assertRaises(exception.ResourceFailure,
|
||||
scheduler.TaskRunner(rsrc.create))
|
||||
self.assertEqual(
|
||||
'NeutronClientException: An unknown exception occurred.',
|
||||
str(error))
|
||||
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
|
||||
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_router_l3_agent_delete_failed(self):
|
||||
clients.OpenStackClients.keystone().AndReturn(
|
||||
fakes.FakeKeystoneClient())
|
||||
|
||||
neutronclient.Client.add_router_to_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
{'router_id': u'2b0347ab-9e42-434f-8249-702eda4ce7a6'}
|
||||
).AndReturn(None)
|
||||
|
||||
neutronclient.Client.remove_router_from_l3_agent(
|
||||
u'5dab1619-9bb0-4e6f-9725-c5e2bfdec434',
|
||||
u'2b0347ab-9e42-434f-8249-702eda4ce7a6'
|
||||
).AndRaise(qe.NeutronClientException(status_code=500))
|
||||
|
||||
self.m.ReplayAll()
|
||||
t = template_format.parse(neutron_l3_agent_template)
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = router.RouterL3Agent('test_router_l3_agent',
|
||||
t['Resources']['router_l3_agent'], stack)
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
error = self.assertRaises(exception.ResourceFailure,
|
||||
scheduler.TaskRunner(rsrc.delete))
|
||||
self.assertEqual(
|
||||
'NeutronClientException: An unknown exception occurred.',
|
||||
str(error))
|
||||
self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
@skipIf(neutronclient is None, 'neutronclient unavailable')
|
||||
class NeutronFloatingIPTest(HeatTestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user