Merge "Reimplement L3Agent as router's property"

This commit is contained in:
Jenkins 2014-03-18 02:32:29 +00:00 committed by Gerrit Code Review
commit 4caec7687e
2 changed files with 91 additions and 198 deletions

View File

@ -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,
}

View File

@ -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())