Merge "Reimplement L3Agent as router's property"
This commit is contained in:
commit
4caec7687e
|
@ -31,8 +31,10 @@ class Router(neutron.NeutronResource):
|
|||
|
||||
PROPERTIES = (
|
||||
NAME, EXTERNAL_GATEWAY, VALUE_SPECS, ADMIN_STATE_UP,
|
||||
AGENT_ID,
|
||||
) = (
|
||||
'name', 'external_gateway_info', 'value_specs', 'admin_state_up',
|
||||
'agent_id',
|
||||
)
|
||||
|
||||
_EXTERNAL_GATEWAY_KEYS = (
|
||||
|
@ -78,6 +80,13 @@ class Router(neutron.NeutronResource):
|
|||
default=True,
|
||||
update_allowed=True
|
||||
),
|
||||
AGENT_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('ID of the L3 agent. NOTE: The default policy setting in '
|
||||
'Neutron restricts usage of this property to administrative '
|
||||
'users only.'),
|
||||
update_allowed=True
|
||||
),
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
|
@ -105,9 +114,15 @@ class Router(neutron.NeutronResource):
|
|||
props = self.prepare_properties(
|
||||
self.properties,
|
||||
self.physical_resource_name())
|
||||
|
||||
agent_id = props.pop(self.AGENT_ID, None)
|
||||
|
||||
router = self.neutron().create_router({'router': props})['router']
|
||||
self.resource_id_set(router['id'])
|
||||
|
||||
if agent_id:
|
||||
self._replace_agent(agent_id)
|
||||
|
||||
def _show_resource(self):
|
||||
return self.neutron().show_router(
|
||||
self.resource_id)['router']
|
||||
|
@ -127,8 +142,26 @@ class Router(neutron.NeutronResource):
|
|||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
props = self.prepare_update_properties(json_snippet)
|
||||
self.neutron().update_router(
|
||||
self.resource_id, {'router': props})
|
||||
|
||||
agent_id = props.pop(self.AGENT_ID, None)
|
||||
|
||||
if self.AGENT_ID in prop_diff:
|
||||
self._replace_agent(agent_id)
|
||||
del prop_diff[self.AGENT_ID]
|
||||
|
||||
if len(prop_diff) > 0:
|
||||
self.neutron().update_router(
|
||||
self.resource_id, {'router': props})
|
||||
|
||||
def _replace_agent(self, agent_id=None):
|
||||
ret = self.neutron().list_l3_agent_hosting_routers(
|
||||
self.resource_id)
|
||||
for agent in ret['agents']:
|
||||
self.neutron().remove_router_from_l3_agent(
|
||||
agent['id'], self.resource_id)
|
||||
if agent_id:
|
||||
self.neutron().add_router_to_l3_agent(
|
||||
agent_id, {'router_id': self.resource_id})
|
||||
|
||||
|
||||
class RouterInterface(neutron.NeutronResource):
|
||||
|
@ -155,16 +188,6 @@ class RouterInterface(neutron.NeutronResource):
|
|||
),
|
||||
}
|
||||
|
||||
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(RouterL3Agent.ROUTER_ID) ==
|
||||
self.properties.get(self.ROUTER_ID)):
|
||||
deps += (self, resource)
|
||||
|
||||
def validate(self):
|
||||
'''
|
||||
Validate any of the provided params
|
||||
|
@ -251,12 +274,6 @@ class RouterGateway(neutron.NeutronResource):
|
|||
resource.properties.get(subnet.Subnet.NETWORK_ID) ==
|
||||
self.properties.get(self.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(RouterL3Agent.ROUTER_ID) ==
|
||||
self.properties.get(self.ROUTER_ID)):
|
||||
deps += (self, resource)
|
||||
|
||||
def handle_create(self):
|
||||
router_id = self.properties.get(self.ROUTER_ID)
|
||||
|
@ -280,55 +297,6 @@ class RouterGateway(neutron.NeutronResource):
|
|||
self._handle_not_found_exception(ex)
|
||||
|
||||
|
||||
class RouterL3Agent(neutron.NeutronResource):
|
||||
PROPERTIES = (
|
||||
ROUTER_ID, L3_AGENT_ID,
|
||||
) = (
|
||||
'router_id', 'l3_agent_id',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
ROUTER_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('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,
|
||||
_('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[self.ROUTER_ID]
|
||||
l3_agent_id = self.properties[self.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 {}
|
||||
|
@ -337,5 +305,4 @@ def resource_mapping():
|
|||
'OS::Neutron::Router': Router,
|
||||
'OS::Neutron::RouterInterface': RouterInterface,
|
||||
'OS::Neutron::RouterGateway': RouterGateway,
|
||||
'OS::Neutron::RouterL3Agent': RouterL3Agent,
|
||||
}
|
||||
|
|
|
@ -91,7 +91,10 @@ neutron_template = '''
|
|||
}
|
||||
},
|
||||
"router": {
|
||||
"Type": "OS::Neutron::Router"
|
||||
"Type": "OS::Neutron::Router",
|
||||
"Properties": {
|
||||
"agent_id": "792ff887-6c85-4a56-b518-23f24fa65581"
|
||||
}
|
||||
},
|
||||
"router_interface": {
|
||||
"Type": "OS::Neutron::RouterInterface",
|
||||
|
@ -146,36 +149,6 @@ 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_external_gateway_template = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
|
@ -1033,6 +1006,8 @@ class NeutronRouterTest(HeatTestCase):
|
|||
'add_router_to_l3_agent')
|
||||
self.m.StubOutWithMock(neutronclient.Client,
|
||||
'remove_router_from_l3_agent')
|
||||
self.m.StubOutWithMock(neutronclient.Client,
|
||||
'list_l3_agent_hosting_routers')
|
||||
self.m.StubOutWithMock(router.neutronV20,
|
||||
'find_resourceid_by_name_or_id')
|
||||
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
|
||||
|
@ -1082,6 +1057,13 @@ class NeutronRouterTest(HeatTestCase):
|
|||
"id": "3e46229d-8fce-4733-819a-b5fe630550f8"
|
||||
}
|
||||
})
|
||||
neutronclient.Client.list_l3_agent_hosting_routers(
|
||||
u'3e46229d-8fce-4733-819a-b5fe630550f8'
|
||||
).AndReturn({"agents": []})
|
||||
neutronclient.Client.add_router_to_l3_agent(
|
||||
u'792ff887-6c85-4a56-b518-23f24fa65581',
|
||||
{'router_id': u'3e46229d-8fce-4733-819a-b5fe630550f8'}
|
||||
).AndReturn(None)
|
||||
neutronclient.Client.show_router(
|
||||
'3e46229d-8fce-4733-819a-b5fe630550f8').AndReturn({
|
||||
"router": {
|
||||
|
@ -1135,6 +1117,41 @@ class NeutronRouterTest(HeatTestCase):
|
|||
})
|
||||
|
||||
# Update script
|
||||
neutronclient.Client.list_l3_agent_hosting_routers(
|
||||
u'3e46229d-8fce-4733-819a-b5fe630550f8'
|
||||
).AndReturn({
|
||||
"agents": [{
|
||||
"admin_state_up": True,
|
||||
"agent_type": "L3 agent",
|
||||
"alive": True,
|
||||
"binary": "neutron-l3-agent",
|
||||
"configurations": {
|
||||
"ex_gw_ports": 1,
|
||||
"floating_ips": 0,
|
||||
"gateway_external_network_id": "",
|
||||
"handle_internal_only_routers": True,
|
||||
"interface_driver": "DummyDriver",
|
||||
"interfaces": 1,
|
||||
"router_id": "",
|
||||
"routers": 1,
|
||||
"use_namespaces": True},
|
||||
"created_at": "2014-03-11 05:00:05",
|
||||
"description": None,
|
||||
"heartbeat_timestamp": "2014-03-11 05:01:49",
|
||||
"host": "l3_agent_host",
|
||||
"id": "792ff887-6c85-4a56-b518-23f24fa65581",
|
||||
"started_at": "2014-03-11 05:00:05",
|
||||
"topic": "l3_agent"
|
||||
}]
|
||||
})
|
||||
neutronclient.Client.remove_router_from_l3_agent(
|
||||
u'792ff887-6c85-4a56-b518-23f24fa65581',
|
||||
u'3e46229d-8fce-4733-819a-b5fe630550f8'
|
||||
).AndReturn(None)
|
||||
neutronclient.Client.add_router_to_l3_agent(
|
||||
u'63b3fd83-2c5f-4dad-b3ae-e0f83a40f216',
|
||||
{'router_id': u'3e46229d-8fce-4733-819a-b5fe630550f8'}
|
||||
).AndReturn(None)
|
||||
neutronclient.Client.update_router(
|
||||
'3e46229d-8fce-4733-819a-b5fe630550f8',
|
||||
{'router': {
|
||||
|
@ -1173,10 +1190,16 @@ class NeutronRouterTest(HeatTestCase):
|
|||
"Type": "OS::Neutron::Router",
|
||||
"Properties": {
|
||||
"admin_state_up": False,
|
||||
"name": "myrouter"
|
||||
"name": "myrouter",
|
||||
"agent_id": "63b3fd83-2c5f-4dad-b3ae-e0f83a40f216"
|
||||
}
|
||||
}
|
||||
rsrc.handle_update(update_snippet, {}, {})
|
||||
prop_diff = {
|
||||
"admin_state_up": False,
|
||||
"name": "myrouter",
|
||||
"agent_id": "63b3fd83-2c5f-4dad-b3ae-e0f83a40f216"
|
||||
}
|
||||
rsrc.handle_update(update_snippet, {}, prop_diff)
|
||||
|
||||
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
|
||||
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
|
||||
|
@ -1344,103 +1367,6 @@ 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()
|
||||
|
||||
def _create_router_with_gateway(self):
|
||||
clients.OpenStackClients.keystone().AndReturn(
|
||||
fakes.FakeKeystoneClient())
|
||||
|
|
Loading…
Reference in New Issue