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.common import exception
|
||||||
from heat.engine import clients
|
from heat.engine import clients
|
||||||
from heat.engine.resources.neutron import neutron
|
from heat.engine.resources.neutron import neutron
|
||||||
|
from heat.engine import properties
|
||||||
from heat.engine import scheduler
|
from heat.engine import scheduler
|
||||||
|
|
||||||
if clients.neutronclient is not None:
|
if clients.neutronclient is not None:
|
||||||
@ -93,6 +94,16 @@ class RouterInterface(neutron.NeutronResource):
|
|||||||
'Description': _('The port id, either '
|
'Description': _('The port id, either '
|
||||||
'subnet_id or port_id should be specified.')}}
|
'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):
|
def validate(self):
|
||||||
'''
|
'''
|
||||||
Validate any of the provided params
|
Validate any of the provided params
|
||||||
@ -157,6 +168,12 @@ class RouterGateway(neutron.NeutronResource):
|
|||||||
resource.properties.get('network_id') ==
|
resource.properties.get('network_id') ==
|
||||||
self.properties.get('network_id')):
|
self.properties.get('network_id')):
|
||||||
deps += (self, resource)
|
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):
|
def handle_create(self):
|
||||||
router_id = self.properties.get('router_id')
|
router_id = self.properties.get('router_id')
|
||||||
@ -181,6 +198,50 @@ class RouterGateway(neutron.NeutronResource):
|
|||||||
raise ex
|
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():
|
def resource_mapping():
|
||||||
if clients.neutronclient is None:
|
if clients.neutronclient is None:
|
||||||
return {}
|
return {}
|
||||||
@ -189,4 +250,5 @@ def resource_mapping():
|
|||||||
'OS::Neutron::Router': Router,
|
'OS::Neutron::Router': Router,
|
||||||
'OS::Neutron::RouterInterface': RouterInterface,
|
'OS::Neutron::RouterInterface': RouterInterface,
|
||||||
'OS::Neutron::RouterGateway': RouterGateway,
|
'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 = '''
|
neutron_floating_template = '''
|
||||||
{
|
{
|
||||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||||
@ -770,6 +800,10 @@ class NeutronRouterTest(HeatTestCase):
|
|||||||
self.m.StubOutWithMock(neutronclient.Client, 'remove_interface_router')
|
self.m.StubOutWithMock(neutronclient.Client, 'remove_interface_router')
|
||||||
self.m.StubOutWithMock(neutronclient.Client, 'add_gateway_router')
|
self.m.StubOutWithMock(neutronclient.Client, 'add_gateway_router')
|
||||||
self.m.StubOutWithMock(neutronclient.Client, 'remove_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,
|
self.m.StubOutWithMock(router.neutronV20,
|
||||||
'find_resourceid_by_name_or_id')
|
'find_resourceid_by_name_or_id')
|
||||||
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
||||||
@ -1083,6 +1117,103 @@ class NeutronRouterTest(HeatTestCase):
|
|||||||
scheduler.TaskRunner(rsrc.delete)()
|
scheduler.TaskRunner(rsrc.delete)()
|
||||||
self.m.VerifyAll()
|
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')
|
@skipIf(neutronclient is None, 'neutronclient unavailable')
|
||||||
class NeutronFloatingIPTest(HeatTestCase):
|
class NeutronFloatingIPTest(HeatTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user